diff --git a/core/http/app.go b/core/http/app.go index e18690def..bfa47c584 100644 --- a/core/http/app.go +++ b/core/http/app.go @@ -167,6 +167,21 @@ func API(application *application.Application) (*echo.Echo, error) { res := c.Response() err := next(c) + // Echo's central HTTPErrorHandler runs *after* this middleware + // returns, so res.Status still reads the default 200 here when a + // handler returned an error without writing a response. Mirror + // echo.DefaultHTTPErrorHandler's status derivation so the access + // log reflects the status the client actually receives — without + // this, every silent handler error logs as 200. + status := res.Status + if err != nil && !res.Committed { + status = http.StatusInternalServerError + var he *echo.HTTPError + if errors.As(err, &he) { + status = he.Code + } + } + // Fix for #7989: Reduce log verbosity of Web UI polling, resources API, and health checks // These paths are logged at DEBUG level (hidden by default) instead of INFO. isQuietPath := false @@ -177,10 +192,10 @@ func API(application *application.Application) (*echo.Echo, error) { } } - if isQuietPath && res.Status == 200 { - xlog.Debug("HTTP request", "method", req.Method, "path", req.URL.Path, "status", res.Status) + if isQuietPath && status == 200 { + xlog.Debug("HTTP request", "method", req.Method, "path", req.URL.Path, "status", status) } else { - xlog.Info("HTTP request", "method", req.Method, "path", req.URL.Path, "status", res.Status) + xlog.Info("HTTP request", "method", req.Method, "path", req.URL.Path, "status", status) } return err } diff --git a/core/http/endpoints/openai/transcription.go b/core/http/endpoints/openai/transcription.go index 2979ecd59..6312e3cb9 100644 --- a/core/http/endpoints/openai/transcription.go +++ b/core/http/endpoints/openai/transcription.go @@ -128,6 +128,15 @@ func TranscriptEndpoint(cl *config.ModelConfigLoader, ml *model.ModelLoader, app tr, err := backend.ModelTranscriptionWithOptions(req, ml, *config, appConfig) if err != nil { + // Log before returning so the underlying error survives. Echo's + // error handler turns this into a 500 with a generic body, which + // otherwise leaves operators chasing a silent failure — see e.g. + // distributed transcription, where the gRPC error from a remote + // node is the only signal of what actually went wrong. + xlog.Error("Transcription failed", + "model", config.Name, + "audio", dst, + "error", err) return err }