From 2e7bf85e7a552d853850ab4b3142415b9bedddd5 Mon Sep 17 00:00:00 2001 From: Dotta Date: Fri, 6 Mar 2026 11:41:06 -0600 Subject: [PATCH] Fix 500 error logs still showing generic pino-http message The previous fix (8151331) set res.err but pino-http wasn't picking it up (likely Express 5 response object behavior). Switch to a custom __errorContext property on the response that customErrorMessage and customProps read directly, bypassing pino-http's unreliable res.err check. Remove duplicate manual logger.error calls from the error handler since pino-http now gets the full context. Co-Authored-By: Claude Opus 4.6 --- server/src/middleware/error-handler.ts | 66 +++++++++++--------------- server/src/middleware/logger.ts | 12 ++++- 2 files changed, 38 insertions(+), 40 deletions(-) diff --git a/server/src/middleware/error-handler.ts b/server/src/middleware/error-handler.ts index 7cf81678..293c42ab 100644 --- a/server/src/middleware/error-handler.ts +++ b/server/src/middleware/error-handler.ts @@ -1,8 +1,16 @@ import type { Request, Response, NextFunction } from "express"; import { ZodError } from "zod"; -import { logger } from "./logger.js"; import { HttpError } from "../errors.js"; +export interface ErrorContext { + error: { message: string; stack?: string; name?: string; details?: unknown; raw?: unknown }; + method: string; + url: string; + reqBody?: unknown; + reqParams?: unknown; + reqQuery?: unknown; +} + export function errorHandler( err: unknown, req: Request, @@ -11,22 +19,14 @@ export function errorHandler( ) { if (err instanceof HttpError) { if (err.status >= 500) { - (res as any).err = err; - logger.error( - { - err: { message: err.message, stack: err.stack, name: err.name, details: err.details }, - method: req.method, - url: req.originalUrl, - reqBody: req.body, - reqParams: req.params, - reqQuery: req.query, - }, - "HttpError %d: %s %s — %s", - err.status, - req.method, - req.originalUrl, - err.message, - ); + (res as any).__errorContext = { + error: { message: err.message, stack: err.stack, name: err.name, details: err.details }, + method: req.method, + url: req.originalUrl, + reqBody: req.body, + reqParams: req.params, + reqQuery: req.query, + } satisfies ErrorContext; } res.status(err.status).json({ error: err.message, @@ -40,28 +40,16 @@ export function errorHandler( return; } - const errObj = err instanceof Error - ? { message: err.message, stack: err.stack, name: err.name } - : { raw: err }; + (res as any).__errorContext = { + error: err instanceof Error + ? { message: err.message, stack: err.stack, name: err.name } + : { message: String(err), raw: err }, + method: req.method, + url: req.originalUrl, + reqBody: req.body, + reqParams: req.params, + reqQuery: req.query, + } satisfies ErrorContext; - // Attach the real error so pino-http uses it instead of its generic - // "failed with status code 500" message in the response-complete log - const realError = err instanceof Error ? err : Object.assign(new Error(String(err)), { raw: err }); - (res as any).err = realError; - - logger.error( - { - err: errObj, - method: req.method, - url: req.originalUrl, - reqBody: req.body, - reqParams: req.params, - reqQuery: req.query, - }, - "Unhandled error: %s %s — %s", - req.method, - req.originalUrl, - err instanceof Error ? err.message : String(err), - ); res.status(500).json({ error: "Internal server error" }); } diff --git a/server/src/middleware/logger.ts b/server/src/middleware/logger.ts index 4c018138..dd826c06 100644 --- a/server/src/middleware/logger.ts +++ b/server/src/middleware/logger.ts @@ -53,11 +53,21 @@ export const httpLogger = pinoHttp({ return `${req.method} ${req.url} ${res.statusCode}`; }, customErrorMessage(req, res, err) { - const errMsg = err?.message || (res as any).err?.message || "unknown error"; + const ctx = (res as any).__errorContext; + const errMsg = ctx?.error?.message || err?.message || (res as any).err?.message || "unknown error"; return `${req.method} ${req.url} ${res.statusCode} — ${errMsg}`; }, customProps(req, res) { if (res.statusCode >= 400) { + const ctx = (res as any).__errorContext; + if (ctx) { + return { + err: ctx.error, + reqBody: ctx.reqBody, + reqParams: ctx.reqParams, + reqQuery: ctx.reqQuery, + }; + } const props: Record = {}; const { body, params, query } = req as any; if (body && typeof body === "object" && Object.keys(body).length > 0) {