Skip to content

Commit 25e5415

Browse files
fix(model): report backend crash diagnostics
Unexpected runtime exits only reported an exit code, which hid the backend diagnostic. Include the final non-empty stderr line when one exists. Assisted-by: Codex:gpt-5
1 parent 44413a9 commit 25e5415

2 files changed

Lines changed: 41 additions & 0 deletions

File tree

pkg/model/process.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,9 @@ func (ml *ModelLoader) startProcess(grpcProcess, id string, serverAddress string
350350
} else {
351351
// A stop we didn't initiate — a SIGSEGV from a missing shared
352352
// library, a Python ImportError, an OOM kill, an unexpected self-exit.
353+
if diagnostic := lastNonEmptyLine(grpcControlProcess.StderrPath(), startupStderrTailBytes); diagnostic != "" {
354+
fields = append(fields, "stderr", diagnostic)
355+
}
353356
xlog.Warn("Backend process exited unexpectedly", fields...)
354357
}
355358
}()

pkg/model/process_exit_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package model
2+
3+
import (
4+
"bytes"
5+
"log/slog"
6+
"os"
7+
"path/filepath"
8+
9+
"github.com/mudler/LocalAI/pkg/system"
10+
"github.com/mudler/xlog"
11+
. "github.com/onsi/ginkgo/v2"
12+
. "github.com/onsi/gomega"
13+
)
14+
15+
var _ = Describe("backend process exit diagnostics", func() {
16+
It("includes the exit code and final stderr line for an unexpected exit", func() {
17+
tmpDir := GinkgoT().TempDir()
18+
backendPath := filepath.Join(tmpDir, "failing-backend")
19+
Expect(os.WriteFile(backendPath, []byte("#!/bin/sh\necho 'first diagnostic' >&2\necho 'fatal metal pipeline error' >&2\nexit 42\n"), 0o700)).To(Succeed())
20+
21+
captured := &bytes.Buffer{}
22+
handler := slog.NewTextHandler(captured, &slog.HandlerOptions{Level: slog.LevelWarn})
23+
xlog.SetLogger(xlog.NewLoggerWithHandler(handler, xlog.LogLevelWarn))
24+
DeferCleanup(func() {
25+
xlog.SetLogger(xlog.NewLogger(xlog.LogLevel("info"), "text"))
26+
})
27+
28+
loader := NewModelLoader(&system.SystemState{Model: system.Model{ModelsPath: tmpDir}})
29+
process, err := loader.startProcess(backendPath, "test-model", "127.0.0.1:65535")
30+
Expect(err).ToNot(HaveOccurred())
31+
Eventually(process.Done()).Should(BeClosed())
32+
Eventually(captured.String).Should(And(
33+
ContainSubstring("Backend process exited unexpectedly"),
34+
ContainSubstring("exitCode=42"),
35+
ContainSubstring(`stderr="fatal metal pipeline error"`),
36+
))
37+
})
38+
})

0 commit comments

Comments
 (0)