feat(observability): server-only Sentry capture, inert without a DSN#600
Conversation
Add @sentry/node behind an optional SENTRY_DSN env var. Without a DSN the SDK is never imported and nothing changes; with one, init happens last in the instrumentation boot guard with privacy scrubbing (no request bodies, headers, breadcrumbs, or PII — clinical query text must never leave the box). Capture points: - onRequestError instrumentation hook for uncaught route/RSC/action errors (path stripped of query strings) - answer + answer/stream route catches, filtered to server-fault (>=500) errors only; client aborts and expected sub-500 degradations are skipped - rag.ts generation-fallback catch reports answer_generation_fallback as a warning event — this silent source-only degradation is invisible to route captures and was the exact signature of the GEN-C1 token-starvation incident All capture flows through a no-op-when-disabled helper (src/lib/observability/error-capture.ts) so routes never import the SDK directly and capture failures can never break a request. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
💤 Files with no reviewable changes (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThis change adds optional server-side Sentry integration, privacy-scrubbed instrumentation, lazy capture helpers, request error reporting, route-level failure capture, and RAG fallback events. Client aborts and non-server errors are excluded from exception reporting. ChangesServer-side Sentry observability
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant AnswerRoute
participant RAG
participant ErrorCapture
participant Sentry
Client->>AnswerRoute: Submit answer request
AnswerRoute->>RAG: Generate answer
RAG->>ErrorCapture: Capture fallback event on degradation
AnswerRoute->>ErrorCapture: Capture qualifying server exception
ErrorCapture->>Sentry: Send exception or warning event
AnswerRoute-->>Client: Return JSON response
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c131b9b7ee
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/app/api/answer/route.ts`:
- Around line 197-204: Honor client aborts across all Sentry capture paths: in
src/app/api/answer/route.ts lines 197-204, compute clientAborted before the
PublicApiError branch and skip captureServerException for aborted requests; in
src/app/api/answer/stream/route.ts lines 102-106, pass the request signal to
logStreamError and guard capture; in src/app/api/answer/stream/route.ts lines
310-320, apply the same client-abort guard to outer catch captures.
- Line 200: Sanitize all exception payloads before calling
captureServerException: update the PublicApiError, generic, and unknown error
paths in src/app/api/answer/route.ts (lines 200, 214-215, and 221-223), and the
corresponding stream error paths in src/app/api/answer/stream/route.ts (lines
106, 311-312, and 315-319). Reuse the established error-normalization/scrubbing
mechanism so no raw clinical error payload is forwarded to Sentry.
In `@src/lib/observability/error-capture.ts`:
- Around line 9-12: Update captureServerException() to sanitize or wrap the
incoming error before passing it to captureException(...), removing potentially
sensitive exception messages and stack-frame content while preserving safe
operational context. Keep the existing privacy boundary and beforeSend behavior
unchanged.
In `@src/lib/rag.ts`:
- Around line 5175-5182: Ensure the answer_generation_fallback telemetry
initiated in the fallback branch is not dropped before capture completes: await
captureServerEvent before returning, or schedule it through the existing after()
mechanism. Preserve the sanitizedReason, queryClass, and routeMode payload.
🪄 Autofix (Beta)
❌ Autofix failed (check again to retry)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 5895c7f6-f43e-43a2-8828-40642202b17e
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (10)
.env.examplepackage.jsonsrc/app/api/answer/route.tssrc/app/api/answer/stream/route.tssrc/instrumentation.tssrc/lib/env.tssrc/lib/observability/error-capture.tssrc/lib/rag.tstests/error-capture.test.tstests/instrumentation.test.ts
|
@copilot resolve the merge conflicts in this pull request |
|
@copilot resolve the merge conflicts in this pull request |
|
Note Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it. ❌ Cannot run autofix: This PR has merge conflicts. Please resolve the conflicts with the base branch and try again. Alternatively, use |
Head branch was pushed to by a user without write access
Resolved — I merged |
Resolved in |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/lib/observability/error-capture.ts`:
- Around line 44-46: Restrict the errorType value in the Sentry capture flow
around captureException to a fixed allowlist of known error names, falling back
to omitting the field or a safe constant for unrecognized names. Ensure custom
Error.name values cannot reach extra.errorType, and add a regression test using
a sensitive custom name to verify it is not sent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 703e87fa-c216-46e3-865d-e234209f65ad
📒 Files selected for processing (7)
src/app/api/answer/route.tssrc/app/api/answer/stream/route.tssrc/lib/env.tssrc/lib/observability/error-capture.tssrc/lib/rag.tstests/error-capture.test.tstests/instrumentation.test.ts
🚧 Files skipped from review as they are similar to previous changes (6)
- src/lib/env.ts
- tests/error-capture.test.ts
- src/app/api/answer/stream/route.ts
- src/lib/rag.ts
- src/app/api/answer/route.ts
- tests/instrumentation.test.ts
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 85300fccc2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex resolve actionable Codex review findings for this pull request and current head using the repository instructions. This is the pull request's single automatic repair pass: do not perform a fresh review, create new standalone findings, or request another review. Work only the existing unresolved Codex threads on the current head. Always fix P0 and P1 findings. For P2 and lower findings, fix only clear, scoped, low-risk issues; otherwise disposition them with a concise reason. After fixing or dispositioning a thread, reply in that thread with as the first line, followed by a concise summary; that marker authorizes the workflow to close that exact thread. If human input or new authorization is required, do not use the marker and leave the thread open with the blocker. Finish only after every actionable thread is fixed or dispositioned and closed, or explicitly left open for a human decision. Do not update the branch from main, address unrelated reviews, broaden scope, or create more than one scoped fix commit. Do not use external APIs, paid services, credentials, dependency changes, or broad refactors unless explicitly authorized. Add targeted tests where behavior changes and run the narrowest relevant validation. |
All actionable findings were fixed, all review threads are resolved, focused privacy tests passed, and required CI is green.
Summary
@sentry/node(MIT) behind an optionalSENTRY_DSNenv var — fully inert when unset: the SDK is never imported, no network egress, no behavior change. Not part ofrequireServerEnv, so a missing DSN never blocks boot.@sentry/nextjswizard, nonext.config.tswrapping, no CSP changes, nothing shipped to the client bundle.sendDefaultPii: false,beforeSendstrips request payloads/headers, breadcrumbs disabled entirely. Events carry only the error plus small operational context (route, status, sanitized reason) — never query text or document content.Capture points
onRequestErrorinstrumentation hook (Next 16 stable API) for uncaught route/RSC/action errors, with query strings stripped from paths./api/answer+/api/answer/streamcatch blocks, filtered to server-fault (≥500) errors; client aborts and expected sub-500 degradations (quota/timeout → source-only fallback) are skipped to avoid noise.rag.tsgeneration-fallback catch reportsanswer_generation_fallbackas a warning event — this silent source-only degradation never reaches route-level capture and was the exact signature of the GEN-C1 token-starvation incident that previously took weeks to spot via manual SLO correlation.src/lib/observability/error-capture.ts); routes never import the SDK directly and capture failures can never break a request.Verification
npm run verify:cheap— green (unit suite incl. 10 new tests acrosstests/instrumentation.test.tsandtests/error-capture.test.ts: DSN-unset no-ops, scrubber behavior, ≥500 filtering, SDK-failure swallowing).npm run build— green, client-bundle secret scan passed (confirms nothing Sentry-related reaches the browser).Operator follow-up (not in this PR)
Create a Sentry project and set
SENTRY_DSNin the Railway service env. The code merges fully dormant until then.Clinical governance preflight
Touches the answer pipeline error paths only — no retrieval, ranking, source rendering, or answer-content changes. Degraded-response behavior is byte-identical; the only addition is telemetry on already-existing failure branches, gated behind an env var that is unset in production today.
🤖 Generated with Claude Code
Summary by CodeRabbit
SENTRY_DSN(documented in.env.example) with privacy scrubbing.