Skip to content

fix: return JSON on unhandled errors (no more HTML 500)#29

Merged
AdaInTheLab merged 1 commit intomainfrom
fix/json-error-handler
Apr 30, 2026
Merged

fix: return JSON on unhandled errors (no more HTML 500)#29
AdaInTheLab merged 1 commit intomainfrom
fix/json-error-handler

Conversation

@AdaInTheLab
Copy link
Copy Markdown
Owner

Summary

Production POST /admin/notes is returning an HTML 500 page with no response body, making it impossible to see the actual exception from a JSON client. That happens because Express's default error handler renders HTML when no JSON error middleware intercepts.

This PR adds a tiny JSON error-handler at the end of `createApp()` so:

  • Unhandled exceptions surface as { ok: false, error: { code, message, details? } }
  • Middleware that throws with err.status / err.statusCode (notably express-openapi-validator, which throws 400 with an .errors[] array) lands as the right HTTP code with the validation details visible
  • A trimmed stack is included outside production
  • The full error is always logged server-side via `console.error`, so PM2 logs continue to capture cause

Why

The 500s started after #28 landed. Without the error body we can't tell whether the failure is:

  • DB column missing (migration didn't fully run on prod)
  • OpenAPI validation rejecting the new author_name / author_kind / tags fields because openapi.json wasn't updated alongside the handler
  • Something earlier in middleware (auth, body parsing)

This handler will surface the actual cause on the next failed POST.

Testing

  • npx tsc --noEmit — clean
  • npm run test — 41/41 pass
  • No existing route behavior changes (handler is only invoked when an error reaches the end of the chain)

Breaking changes

None for happy-path responses. Error response shape is now JSON instead of HTML — anything that was relying on parsing the HTML 500 page (nothing should be) would break. Worth it.

Next step

Merge + deploy, then re-run the failing POST. The response body will tell us what to fix.

Co-authored-by: Sage [email protected]

… default)

Express's default error handler renders an HTML page for 5xx, which
hides the actual cause from anyone consuming the API as JSON
(browser fetch, CLI, debugging tools). Add a JSON error-handler
middleware as the last app.use() so failures surface as a structured
{ ok: false, error: { code, message, details? } } response.

Behavior:
- Honors err.status / err.statusCode so middleware that throws with a
  status (e.g. express-openapi-validator with 400 + .errors[]) lands
  as the right code instead of a generic 500
- Maps common statuses to canonical codes (bad_request, unauthorized,
  forbidden, not_found, internal_error)
- Surfaces validation .errors[] as `details` for OpenAPI failures
- Includes a trimmed stack outside production (NODE_ENV !== "production")
- Always logs the full error server-side so PM2 logs still show cause

Triggered by a production HTML-500 from POST /admin/notes that gave
no response body to debug from. With this in place, the next failure
will return the actual exception in JSON.

Co-authored-by: Sage <[email protected]>
@github-actions
Copy link
Copy Markdown

😼🔥 Carmel Chaos Stamp™

🔥 Carmel Chaos Stamp™
😼🔥💬 "I sense nonsense. Proceed."

PR: #29fix: return JSON on unhandled errors (no more HTML 500)
Author: @AdaInTheLab

This automated judgment has been issued by the Chief Judgment Office (CJO).

@AdaInTheLab AdaInTheLab merged commit 47f8d0f into main Apr 30, 2026
2 checks passed
@AdaInTheLab AdaInTheLab deleted the fix/json-error-handler branch April 30, 2026 07:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant