← Blog
ENGINEERING4 min read

A rate limiter that refuses when its store times out, now in all 15 apps

Oct 30, 2026DekimuAI-generated

@dekimuhq/ratelimit 3.0.0 refuses a request when its Redis counter times out, instead of letting it through. Version 3.0.0 was published on Oct 28, 2026 and merged into all 15 consuming apps the same day, with no opt-outs. Adopting it surfaced two bugs the old fail-open behaviour had been quietly covering.

Why should a timeout mean no?

A limiter exists for the moment someone is hammering a route. That is also the moment the counter store is most likely to be slow. If a slow store turns into a free pass, the limit disappears exactly when it is needed.

So the default flipped. A Redis timeout is now a refusal, and the caller decides what a refusal looks like for its own route. The change shipped together with feedback 3.0.0, which builds on the same limiter, and five patch releases raising the zod floor for GHSA-mrhx-c7xc-m3w5.

What did adopting it find?

Making refusal possible meant reading every call site for what happens next. Two of them were wrong in ways that had never mattered before.

InvoiceUp's inbound receipts webhook answered 200 when the limiter timed out. That 200 told the sender the delivery was handled, and the receipt was dropped. It now answers 503, so the sender retries. In Hub, the intake form called `redirect()` inside a `try/catch {}`. The redirect threw, the empty catch swallowed it, and an over-limit submission was stored anyway. Both are fixed.

A refusal is only safe if every caller knows how to hear it.

Which routes stay fail-open?

Not all of them, on purpose. The dekimu-site lead form and the verify.dekimu.com playground mint had their own counters that failed open; both now refuse on their own timeouts. The status board's check endpoint refuses on a Redis timeout or error too, and it now keys on the last hop of the forwarded-for header, since the first hop is something a client can write.

Consent and DSA notices and plain read routes stay fail-open. Refusing a visitor's cookie choice or a page view because a counter was slow costs more than the abuse it would stop.

What is still open?

Some pre-existing own-counter limiters still fail open, in the aiterms scanner and in id's client-error route. They are logged as open items rather than folded into this release. The package side is done; the remaining work is route-by-route judgement about what a refusal should say.

ENGINEERING

This post was drafted by an AI system from Dekimu's public engineering record and published with automated checks, without per-post human editing.

← Back to blog