fix(startup): validate before serving (BUG-025)#111
Conversation
…a traceback BUG-025: the ARCH-057 sequence ran inside the ASGI lifespan, so a failed step's `raise SystemExit(1)` travelled out into uvicorn, which logged a raw Python traceback next to the structured [STARTUP ERROR]. NFR-009 asks for the remediable message instead of the stack dump, and it applied to all 11 steps. Move the sequence into a loop-agnostic `_run_startup()` and add a `main()` container entrypoint (`python -m vektra_app.main`) that runs validation in the very loop that will serve, then calls uvicorn with `lifespan="off"`. A failed step now logs its [STARTUP ERROR] and exits non-zero with no ASGI involvement and no traceback; startup and serving share one event loop, so async resources built during validation stay bound to the loop that serves requests. The ASGI lifespan keeps validating (and raising SystemExit) for the in-process /TestClient path, so `test_missing_llm_provider_aborts_startup` is unchanged. Remove the now-passing `xfail(strict=True)` on `test_no_raw_traceback_on_startup_failure`. Proven on the container: a config-validation failure and a database_connectivity failure each emit the structured error, exit 1, and log no traceback; a valid config still reaches `startup_complete` and serves. Traceability: REQ-011, NFR-009, ARCH-057, DEBT-031 (found by), BUG-024 (same surface) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 18 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
📝 WalkthroughWalkthroughStartup validation is centralized for ASGI and container execution. The container now runs the module entrypoint, which validates before starting uvicorn. Lifespan teardown is shared, startup failures return non-zero without an ASGI traceback, and tests and documentation reflect the new behavior. ChangesStartup validation and serving
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Docker as docker/entrypoint.sh
participant Main as vektra_app.main
participant Startup as _run_startup
participant Uvicorn as uvicorn Server
Docker->>Main: execute python -m vektra_app.main
Main->>Startup: validate configuration and initialize services
Startup-->>Main: success or StartupValidationError
Main->>Uvicorn: start server after successful validation
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request refactors the application startup process to run the 11-step validation (ARCH-057) before starting the Uvicorn server, preventing Python tracebacks on startup failures (BUG-025). The container entrypoint now executes the module directly via python -m vektra_app.main instead of running Uvicorn directly. Review feedback suggests safely accessing app.state.registry during shutdown to avoid potential AttributeErrors if startup fails early, and allowing the host and port to be configured via environment variables rather than being hardcoded.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
fvadicamo
left a comment
There was a problem hiding this comment.
Addressed the two automated review comments (gemini). Both are declined as by-design, with rationale posted in the inline threads and threads resolved:
_run_shutdowndirectapp.state.registryaccess: teardown is only reachable after_run_startupsucceeds (finally-after-yield inlifespan, finally-after-serve in_serve), so the attribute is always set; agetattrfallback would mask a broken invariant without actually making a never-started app's teardown safe.- Hardcoded
0.0.0.0:8000inmain(): preserves the prior entrypoint exactly; the container's internal bind port is intentionally fixed at 8000 (Dockerfile healthcheck, compose:8000mapping, CI probe all depend on it).VEKTRA_PORTis the host-side published port, not the bind port.
CodeRabbit reported no actionable comments. Both integration jobs (pgvector, qdrant) pass, exercising the fix on a real container.
Records the validate-before-serving fix (PR #111): the ARCH-057 sequence now runs in main() ahead of uvicorn (lifespan="off") so a failed step exits with its structured [STARTUP ERROR] and no traceback; the lifespan keeps the SystemExit path for the in-process test. Notes the uvicorn 0.36 get_loop_factory() gotcha found on the container, and the on-container proof. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds an Unreleased > Fixed entry for the validate-before-serving change, matching the manually maintained Keep a Changelog convention. The existing config (min_length) entry noted BUG-025 as "filed, not fixed here"; this records the fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
BUG-025: the ARCH-057 startup validation ran inside the ASGI lifespan, so a failed step's
raise SystemExit(1)propagated into uvicorn, which logged a raw Python traceback next to the structured[STARTUP ERROR]. NFR-009 asks for the remediable message instead of the stack dump, and this affected all 11 steps, not just config validation.Root cause
The container ran
uvicorn vektra_app.main:app, so the 11-step sequence necessarily ran as ASGI startup. Any abort had to travel through uvicorn, which logs a lifespan exception as a traceback beforeApplication startup failed. Exiting.Fix
_run_startup()that raisesStartupValidationError(noSystemExitinside it) and a matching_run_shutdown().main()container entrypoint (python -m vektra_app.main, wired indocker/entrypoint.sh) that runs validation in the very loop that will serve, then calls uvicorn withlifespan="off". A failed step logs its[STARTUP ERROR]and exits non-zero with no ASGI and no traceback. Startup and serving share one event loop, so async resources built during validation (DB engine, HTTP clients) stay bound to the loop that serves requests; uvloop is preserved viaconfig.get_loop_factory().SystemExit) for the in-process / TestClient path, sotest_missing_llm_provider_aborts_startupis unchanged.Testing
make lintandmake testgreen (771 passed). Removed the now-passingxfail(strict=True)ontests/test_startup.py::test_no_raw_traceback_on_startup_failure.Proven on the container (rebuilt image, throwaway
docker compose run, dev stack left untouched):VEKTRA_LLM_PROVIDER(config_validation)[STARTUP ERROR] Step: config_validation, exit 1, no traceback[STARTUP ERROR] Step: database_connectivity, exit 1, no traceback.envstartup_complete,Uvicorn running on http://0.0.0.0:8000, no tracebackAcceptance criteria (BUG-025)
[STARTUP ERROR]and noTraceback (most recent call last)xfail(strict=True)is removedNotes
Integration tests + NFR gates,CI gate) are unchanged. The integration job runs the container andtest_startup.py, which now exercises the fix end-to-end.Backlog: BUG-025. Traceability: REQ-011, NFR-009, ARCH-057, DEBT-031 (found by), BUG-024 (same surface).
🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
[STARTUP ERROR]message and exit cleanly without an ASGI traceback.Documentation