Skip to content

fix(server): serialize startup error so docker logs are debuggable#843

Merged
buremba merged 1 commit into
mainfrom
fix/printable-startup-error
May 18, 2026
Merged

fix(server): serialize startup error so docker logs are debuggable#843
buremba merged 1 commit into
mainfrom
fix/printable-startup-error

Conversation

@buremba
Copy link
Copy Markdown
Member

@buremba buremba commented May 18, 2026

Summary

Top-level main().catch in packages/server/src/server.ts logged the boot error directly with logger.error({ error }, 'Failed to start server'). When the pino error serializer isn't picked up (older image, bundler tree-shake, or before #767 landed in a given build), pino falls back to plain object serialization — and JSON.stringify(new Error('boom')) returns {} because Error properties are non-enumerable.

Docker users (issue #766) saw exactly this and had zero signal about which env var or config field was wrong:

{"level":"error","msg":"Failed to start server","error":{}}

This PR adds a small serializeBootError(err) walker right at the catch site that pulls out:

  • type (constructor name)
  • message
  • stack
  • code (if present, e.g. ECONNREFUSED)
  • issues (ZodError field-path array)
  • errors (AggregateError children, recursively)
  • cause (recursively)

…and logs the result under both err (pino convention) and error (legacy key) so the line is informative regardless of pino config. As a last-resort fallback it also writes a plain-text Failed to start server: <Type>: <message> + stack to stderr, so users whose log shipper only captures stderr or strips structured fields still see the cause.

Reproducer

Pre-fix behavior (what #766 hits):

$ bun -e 'import pino from "pino"; const l = pino(); l.error({error: new Error("ENCRYPTION_KEY is required")}, "Failed to start server")'
{"level":50,"time":...,"error":{},"msg":"Failed to start server"}

Post-fix behavior (same scenario after serializeBootError):

{"level":50,"time":...,
 "err":{"type":"Error","message":"ENCRYPTION_KEY is required","stack":"Error: ENCRYPTION_KEY is required\n    at ..."},
 "error":{...same...},
 "msg":"Failed to start server"}
Failed to start server: Error: ENCRYPTION_KEY is required
Error: ENCRYPTION_KEY is required
    at ...

The original error: {} line is replaced by a fully-structured object with type / message / stack, plus a stderr fallback for docker logs consumers.

Test plan

  • bunx tsc --noEmit -p packages/server/tsconfig.json clean
  • Pre-commit biome + tsc hook passes
  • Manual repro: boot with dotenv.config() returning empty + DATABASE_URL set + no ENCRYPTION_KEY. Pre-fix prints error:{}; post-fix prints full message + stack + stderr fallback line.
  • Optional: trigger any other early-boot throw (ZodError, missing DATABASE_URL) and confirm the new fields land.

Fixes #766

Summary by CodeRabbit

  • Bug Fixes
    • Improved error reporting and logging when the server fails to start, providing clearer and more structured error information for diagnostics.

Review Change Stack

Top-level `main().catch` was logging `{ error }` directly, and
`JSON.stringify(new Error())` returns `{}` because Error's own
properties are non-enumerable. Docker users saw

  {"level":"error","msg":"Failed to start server","error":{}}

with zero signal about which env var or config field was the cause
(issue #766).

Walk the error manually (type / message / stack / code / ZodError
issues / AggregateError children / cause chain) before logging,
and also write a plain-text "Failed to start server: <type>: <msg>"
line to stderr as a fallback for any log shipper that drops the
structured fields.

Fixes #766
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ee096360-1b11-4401-8239-cb6f7c07ed83

📥 Commits

Reviewing files that changed from the base of the PR and between 8102b9e and f56ab94.

📒 Files selected for processing (1)
  • packages/server/src/server.ts

📝 Walkthrough

Walkthrough

This PR adds a serializeBootError helper function and updates the server's top-level error handler to provide structured, debuggable error output during boot failures. The serializer recursively transforms errors and Zod validation issues into JSON-compatible objects; the catch handler logs both structured and plain-text output before exiting.

Changes

Boot Error Serialization

Layer / File(s) Summary
Error serialization and logging
packages/server/src/server.ts
Introduces serializeBootError to defensively convert boot failures—including nested cause, errors, and Zod issues—into enumerable plain objects. Updates the main().catch handler to serialize errors, log structured JSON under both err and error keys, emit plain-text stderr output with message, type, and stack when available, then exit with status 1.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 Errors wrapped in JSON bright,
No more crashes in the night!
Stack traces flow, nested deep—
Debugging logs the server keep. 🌙

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/printable-startup-error

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Comment @coderabbitai help to get the list of available commands and usage tips.

@buremba buremba merged commit 520bbfc into main May 18, 2026
17 of 18 checks passed
@buremba buremba deleted the fix/printable-startup-error branch May 18, 2026 01:52
@codecov-commenter
Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

buremba added a commit that referenced this pull request May 18, 2026
External user (#766) tried docker-compose against the published
ghcr.io/lobu-ai/lobu-app image, missing required env (ENCRYPTION_KEY,
BETTER_AUTH_SECRET) and unable to debug because the boot error logged
as empty `{}`. PR #843 fixed the error logging. This commit fills the
remaining gap: a working sample compose, a self-hosting doc that
spells out what's actually required vs optional, and a .env.example
that no longer claims ANTHROPIC_API_KEY is required.

Image itself is unchanged (already generic enough). Documents the
provider-agnostic reality: 17 providers in config/providers.json,
no env-var required at boot for any of them, runtime provisioning
via admin UI works fine.

Refs #766.
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.

Docker deployment crashes with no error message — impossible to debug locally

2 participants