Summary
validateBearerForRateLimit (src/auth/rate-limit.ts) is called from the app.use("*", ...) rate-limit middleware, ahead of every route handler, to decide whether an incoming bearer token should be treated as an authenticated identity (its own rate-limit bucket) or fall back to IP-keying. It calls authenticatePrivateToken → authenticateSessionToken → getAuthSessionByTokenHash, which does a real D1/Drizzle read against auth_sessions.
Unlike the Durable Object rate-limit-bucket call just below it in checkRateLimitBucket (explicitly fail-open per #5000, since no app.onError is registered anywhere in the app), this DB read is unguarded. If it throws (a transient D1 hiccup, a driver error, or — as hit in tests — a DB binding that doesn't fully implement the D1 contract), the error propagates out of the middleware uncaught. With no global error handler, Hono falls back to a bare, non-JSON Internal Server Error response for whatever route happened to be hit — indistinguishable from an application bug in that route's own handler.
This is purely an identity-classification path (which bucket to count a request against), not the actual authorization decision for the route — those checks happen later in dedicated middleware and re-validate independently. A hiccup here should never be able to take down an otherwise-healthy, otherwise-correctly-authenticated route.
Reproduction
test/integration/orb-ingest.test.ts — "tolerates a list query that omits results (rows.results ?? [])"
Swap env.DB for a minimal stub implementing only .prepare().all() (no .bind()), then hit GET /v1/internal/orb/instances with Authorization: Bearer dev-internal-token. Response body is the literal string Internal Server Error, not JSON — TypeError: this.stmt.bind is not a function thrown from drizzle-orm/d1/session.js inside getAuthSessionByTokenHash, invoked via the rate-limit middleware's validateBearerForRateLimit, before the route's own /v1/internal/* bearer check (which only does a static-token compare and never touches the DB) even runs.
Fix
Wrap validateBearerForRateLimit's body in try/catch, matching the existing fail-open convention in the same file (checkRateLimitBucket's DO-fetch catch, and isGlobalAgentFrozen in src/db/repositories.ts): on error, log and fall back to treating the token as unrecognized (IP-keyed identity), rather than letting the error escape the middleware.
Summary
validateBearerForRateLimit(src/auth/rate-limit.ts) is called from theapp.use("*", ...)rate-limit middleware, ahead of every route handler, to decide whether an incoming bearer token should be treated as an authenticated identity (its own rate-limit bucket) or fall back to IP-keying. It callsauthenticatePrivateToken→authenticateSessionToken→getAuthSessionByTokenHash, which does a real D1/Drizzle read againstauth_sessions.Unlike the Durable Object rate-limit-bucket call just below it in
checkRateLimitBucket(explicitly fail-open per #5000, since noapp.onErroris registered anywhere in the app), this DB read is unguarded. If it throws (a transient D1 hiccup, a driver error, or — as hit in tests — a DB binding that doesn't fully implement the D1 contract), the error propagates out of the middleware uncaught. With no global error handler, Hono falls back to a bare, non-JSONInternal Server Errorresponse for whatever route happened to be hit — indistinguishable from an application bug in that route's own handler.This is purely an identity-classification path (which bucket to count a request against), not the actual authorization decision for the route — those checks happen later in dedicated middleware and re-validate independently. A hiccup here should never be able to take down an otherwise-healthy, otherwise-correctly-authenticated route.
Reproduction
Swap
env.DBfor a minimal stub implementing only.prepare().all()(no.bind()), then hitGET /v1/internal/orb/instanceswithAuthorization: Bearer dev-internal-token. Response body is the literal stringInternal Server Error, not JSON —TypeError: this.stmt.bind is not a functionthrown fromdrizzle-orm/d1/session.jsinsidegetAuthSessionByTokenHash, invoked via the rate-limit middleware'svalidateBearerForRateLimit, before the route's own/v1/internal/*bearer check (which only does a static-token compare and never touches the DB) even runs.Fix
Wrap
validateBearerForRateLimit's body in try/catch, matching the existing fail-open convention in the same file (checkRateLimitBucket's DO-fetch catch, andisGlobalAgentFrozenin src/db/repositories.ts): on error, log and fall back to treating the token as unrecognized (IP-keyed identity), rather than letting the error escape the middleware.