fix(svc): mask internal error text on the wire #31
No reviewers
Labels
No labels
adr
android
area/calendar
area/design-system
area/i18n
area/jobs
area/offline
area/server
area/testing
bug
ci
duplicate
enhancement
help wanted
invalid
notifications
question
reliability
security
severity/low
severity/medium
tracking
web
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
eagraiclainne/app!31
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/internal-error-mask"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Driver/DB error strings rode
CodeInternalto clients ("database is locked", pgx constraint names) — including to unauthenticated callers via the auth interceptor's DB-failure branches.New
MaskInterceptor, outermost in the chain (mask → auth → sanitize → hydrate → otel), so it sees the FINAL error including ones the auth interceptor raises. AnyCodeInternalerror is logged in full server-side (error_id, procedure, original text) and replaced on the wire withinternal error [id=<16-hex>]— code preserved, details and metadata dropped. Unary and streaming paths both covered.Strictly scoped to
CodeInternal: validation/domain/auth/not-found errors pass by identity, so the "failures name their reason" rule holds where it matters. No call-site churn — handlers keep rich%wwrapping for the server log. Otel stays innermost and traces the unmasked error.AGENTS.md gains the seam as service-pattern item 8. Full suite + integration pass.
Fixes #17
🤖 Generated with Claude Code
CodeInternal errors wrapped whatever the failing layer said, so driver strings ("database is locked", pgx constraint names) crossed the wire — including to unauthenticated callers via the auth interceptor's DB-failure branches. A structural MaskInterceptor, outermost in the chain so it sees the final form of every error (auth's included), replaces the text with "internal error [id=…]" and logs the original under the correlation id: operator logs stay rich, the wire stays generic, and details/metadata are dropped with the text since a fresh error carries none. Handlers keep their %w wrapping untouched — the mask is one seam, like sanitize, with no call-site churn. Scoped strictly to CodeInternal so the failures-name-their-reason rule holds: domain, validation, auth and not-found errors stay verbatim, and "internal error" is an internal fault's honest reason — the id makes it findable. Fixes #178763edf69f901a9fcbde