Skip to content

fix(sentry): group Drizzle errors by underlying cause, not SQL text#2957

Merged
RSO merged 3 commits into
mainfrom
fix/sentry-drizzle-cause-as-primary
Apr 30, 2026
Merged

fix(sentry): group Drizzle errors by underlying cause, not SQL text#2957
RSO merged 3 commits into
mainfrom
fix/sentry-drizzle-cause-as-primary

Conversation

@kilo-code-bot
Copy link
Copy Markdown
Contributor

@kilo-code-bot kilo-code-bot Bot commented Apr 30, 2026

Summary

Fix Sentry grouping for Drizzle query errors in apps/web/sentry.server.config.ts.

The existing beforeSend already set a good fingerprint for Drizzle errors (based on the pg error code and cause message), but the reported event's primary exception was never modified — it still had Drizzle's Failed query: <unique SQL> message. Sentry uses the last entry in event.exception.values for the issue title and as a grouping signal, so the title on every Drizzle-wrapped error was a unique SQL string and the actual root cause (e.g. statement timeout) wasn't visible at a glance.

This PR rewrites the root exception value so its type and value reflect error.cause (the underlying pg error), and moves the failed query + params into a new drizzle_query context so the SQL remains visible on the issue without polluting the title or affecting grouping.

Before

  • Issue title: DrizzleQueryError: Failed query: SELECT … WHERE id = $1 … (unique per query site — breaks grouping)
  • error.cause (real root cause, e.g. statement timeout) only surfaced via the fingerprint tuple and as a nested exception further down the chain.

After

  • Issue title: <CauseType>: canceling statement due to statement timeout (or whatever cause.message / cause.name is) — stable across call sites.
  • Full failed query + params + original wrapper message are attached under the drizzle_query context, visible on the Sentry issue page.
  • The wrapper's stack trace (pointing at our application code where db.<op>() was called) is preserved — we only mutate type/value, not stacktrace.
  • Existing fingerprint and db.error_code tag behaviour is unchanged.
  • TRPC 4xx filtering, non-Drizzle errors, and Drizzle errors without a cause are unaffected.

Verification

  • Reviewed the Sentry JS SDK exception payload spec (chained exceptions are ordered oldest → newest, with the last entry being the root used for titling/grouping) to confirm the mutation targets the correct array entry.
  • Inspected an existing repro in apps/web/src/routers/admin/credit-campaigns-router.ts that confirms Drizzle places the pg error (with code, message) on .cause.
  • Skipped pnpm typecheck per repo AGENTS guidance (full typecheck is too slow for targeted changes); ran pnpm format.

Visual Changes

N/A — server-side Sentry reporting only.

Reviewer Notes

  • The causeTypeName helper walks cause.namecause.constructor.name → falls back to 'DatabaseError'. Drizzle's cause is typically a pg DatabaseError, which has a meaningful name, so the title will usually end up as error: <pg message> or DatabaseError: <pg message> depending on how pg sets name in the running version.
  • We deliberately keep the outer (wrapper) stack trace rather than trying to substitute the cause's stack, because the wrapper's frames point to our application code where the query was issued, which is what a responder usually wants. The pg-internal stack is low value.
  • event.contexts.drizzle_query includes params. These are already covered by the existing normalizeDepth: 5 limit in init, and beforeSend runs after PII scrubbing integrations, so this matches the pre-existing treatment of query data in the issue payload (the SQL text was already reaching Sentry via the exception message).

Built for Remon Oldenbeuving by Kilo for Slack

Drizzle wraps query errors with a 'Failed query: <unique SQL>' message.
beforeSend set a useful fingerprint, but the event's primary exception
(used for the issue title) still carried the unique SQL string, so
grouping regressed and the root cause (e.g. 'statement timeout') wasn't
visible in the title.

Rewrite the root exception value (last entry in event.exception.values,
per Sentry's oldest-to-newest ordering) so its type/message reflect
error.cause, and move the failed query + params into the drizzle_query
context so they stay visible on the issue without polluting the title
or fingerprint. Non-Drizzle errors and Drizzle errors without a cause
are unaffected.
Comment thread apps/web/sentry.server.config.ts Outdated
@kilo-code-bot
Copy link
Copy Markdown
Contributor Author

kilo-code-bot Bot commented Apr 30, 2026

Code Review Summary

Status: 1 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/web/sentry.server.config.ts 108 Raw Drizzle query params may leak sensitive values into Sentry.

Fix these issues in Kilo Cloud

Other Observations (not in diff)

Issues found in unchanged code that cannot receive inline comments:

File Line Issue
Files Reviewed (1 files)
  • apps/web/sentry.server.config.ts - 1 issue

Reviewed by gpt-5.5-20260423 · 224,613 tokens

@RSO RSO enabled auto-merge (squash) April 30, 2026 13:07
@RSO RSO merged commit d41824f into main Apr 30, 2026
12 checks passed
@RSO RSO deleted the fix/sentry-drizzle-cause-as-primary branch April 30, 2026 13:08
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.

2 participants