{"record":{"id":"aa178d055ab5790f","repo":"Budibase/budibase","slug":"err-message-aa178d","errorCode":null,"errorMessage":"err.message","messagePattern":"err\\.message","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/backend-core/src/middleware/errorHandling.ts","lineNumber":15,"sourceCode":"import { APIError } from \"@budibase/types\"\nimport * as errors from \"../errors\"\nimport environment from \"../environment\"\nimport { stringContainsSecret } from \"../security/secrets\"\nimport { ParameterizedContext, Next } from \"koa\"\n\nexport async function errorHandling(ctx: ParameterizedContext, next: Next) {\n  try {\n    await next()\n  } catch (err: any) {\n    const status = err.status || err.statusCode || 500\n    ctx.status = status\n\n    if (status >= 400 && status < 500) {\n      console.warn(err)\n    } else {\n      console.error(\"Got 5xx response code\", err)\n    }\n\n    let error: APIError = {\n      message: err.message,\n      status,\n      validationErrors: err.validation,\n      error: errors.getPublicError(err),\n    }\n\n    if (stringContainsSecret(JSON.stringify(error))) {\n      error = {\n        message: \"Unexpected error\",\n        status,\n        error: \"Unexpected error\",\n      }\n    }","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/backend-core/src/middleware/errorHandling.ts#L1-L33","documentation":"This is the global Koa error-handling middleware in backend-core. It doesn't throw its own error; it catches everything downstream, derives status from err.status || err.statusCode || 500, logs (warn for 4xx, error for 5xx), and serializes { message: err.message, status } as the API error response body. The observed message \"err.message\" simply means the original thrown error's message is being surfaced to the client verbatim.","triggerScenarios":"Any middleware or controller throwing an Error/HTTPError: the client receives the error's message field with status err.status/err.statusCode, defaulting to 500 when neither is set.","commonSituations":"Seeing a raw internal error message (e.g. a Postgres/CouchDB error string) in an API response because a low-level error was thrown without a status; debugging why a 500 response carries an unexpected message; unhandled promise rejections inside route handlers.","solutions":["Read the message in the response body — it identifies the actual failing operation; check server logs (console.warn/error) for the full stack","Throw HTTPError with an explicit status in application code so clients get meaningful 4xx codes instead of 500","Wrap low-level DB/HTTP errors in domain errors before they reach middleware","For 5xx, check downstream services (database, SMTP, external APIs) indicated by the message"],"exampleFix":"// before\nthrow new Error(\"connection refused\")\n// after\nthrow new HTTPError(\"Database unavailable, please try again later\", 503)","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"try {\n  await api.request(...)\n} catch (e) {\n  const status = e.status || e.statusCode || 500\n  if (status >= 500) {\n    // retry or check server logs for the full stack\n  } else {\n    // surface e.message to the user\n  }\n}","preventionTips":["Throw HTTPError with explicit status codes in application code","Wrap low-level DB/HTTP errors in domain errors with friendly messages","Avoid leaking internal error strings to clients — map them to user-safe messages","Monitor 5xx rates and inspect console.error output server-side"],"tags":["middleware","error-handling","koa","api"],"backgroundTag":"unhandled-server-error","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}