Return 400 for an invalid audit cursor instead of 500 - #426
Merged
Conversation
decodeCursor threw a generic Error that escaped the admin audit-events route's AuditQueryError catch as an unhandled 500. A stale or hand-edited bookmark is a caller error, like a bad from/to/limit which already answers 400. Throw AuditQueryError from decodeCursor and validate the cursor up front in auditQueryFromUrl, so the route answers 400 with a cursor message before reaching the store.
Ayush7614
requested review from
MikeRyanDev,
davidmckayv,
guidovizoso and
tylerslaton
as code owners
September 7, 2026 15:41
davidmckayv
approved these changes
Sep 8, 2026
davidmckayv
left a comment
Contributor
There was a problem hiding this comment.
Deep-reviewed against live code (correctness, governance, no vendor/secret/scale issues). Composed build+tests green. CHANGELOG/format rebase on CI-validated substance.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
decodeCursorinserver/src/audit.tsthrew a genericErrorthat escaped the admin audit-events route'sAuditQueryErrorcatch as an unhandled 500. A stale or hand-edited bookmark is a caller error, like a badfrom/towhich already answers 400 since #378.This throws
AuditQueryErrorfromdecodeCursorand validates the cursor up front inauditQueryFromUrl, soGET /api/admin/audit-events?cursor=…answers400 { error: cursor must be a valid audit page cursor }before reaching the store.Why it matters
API contract gap on an admin-visible endpoint: corrupt pagination state produced 500s and log noise instead of a 400 the surface can explain. Fail-fast validation also keeps a bad cursor from ever reaching the database query builder.
Verification
server/tests/audit-cursor.test.ts: corrupt cursor throwsAuditQueryError; valid cursor still parses; route returns 400 with the cursor message without touching the store; valid cursor still pages (200).bun test server/tests/audit-cursor.test.ts— 4 pass.bun run --filter server typecheck— clean. Biome format + lint — clean.