Pick a failure.
Prove you can fix it.
Playable debugging incidents with real files, runnable checks, and a clear finish line. No trivia. No toy editor.
More filtersExpand
Playable incidents
145 results
AccessDenied at 3AM
Production job fails only after rotating credentials.
App.render rejects null and undefined options values
A caller invokes `app.render(viewName, options, callback)` and expects the callback to deliver the rendered template string even when `options` is `null` or `undefined`. The callback does not deliver the expected rendered output for those cases, while rendering with a normal options object continues to succeed.
Asset Cache Bust On Stale Rev
Static asset URLs ship with the dev revision because the build id was captured at import time.
Async dedupe race
Two concurrent retries of the same webhook event both pass the dedupe check and both run the side effect because the set is mutated after an await.
Attempt Counter Reset Mid-Loop
Retry helper never reaches its attempt budget because the counter is reset between tries.
Await Missing in Fire-and-Forget
Batch runner swallows async failures because the per-item call is not awaited.
Backoff Jitter RNG Reset Per Attempt
PRNG factory is invoked per attempt, so every jittered backoff collapses to the same first draw.
Backoff Overflow at High Attempts
Exponential backoff returns Infinity once the attempt count gets large enough to overflow the exponent.
Cache invalidated before the write commits
Update helper primes the cache with the pre-write snapshot, so readers see the old value after every update.
Cache invalidation skipped on write
Writes update the database but the cache is never told, so reads keep returning the old value.
Cache key delimiter collision
Two cache lookups that should be distinct reduce to the same string and overwrite each other.
Cache key missing tenant id
Multi-tenant cache key uses just user_id, causing cross-tenant leaks.
Cache mutation leaks across tenants
Cached objects are handed out by reference; one tenant's mutation rewrites what other tenants read.
Cache Stampede
Every morning at 9AM service slows to a crawl for 90 seconds.
Cache stampede without coalescing
Concurrent cold-cache callers each kick off their own expensive compute, overwhelming the worker.
Cache TTL: ms vs seconds
In-memory cache treats milliseconds as seconds on read, so every entry expires on the next call.
Circuit Half-Open Floods Requests
Circuit breaker admits every incoming request while half-open instead of a single probe, hammering the recovering downstream.
Content-Type charset lie
Body decoder ignores the declared charset and always assumes UTF-8.
CORS allows the null origin unconditionally
CORS echoes Access-Control-Allow-Origin: null when the request has no Origin header, exposing the API to sandboxed iframes and file:// pages.
CORS origin parsing bug
Regex origin validation allows malicious domains.
CORS startsWith() allows lookalike origins
CORS allowlist uses `String.prototype.startsWith`, so any domain beginning with our trusted host passes.
Count that missed the where clause
A dashboard count helper ignores its filter and reports the wrong total.
Cursor built from a non-unique column
Pagination cursor encodes only `status`, causing rows with duplicate statuses to be skipped or repeated.
Deadline Truncated Silently
Deadline-aware helper returns the partial result instead of rejecting when the injected clock advances past the deadline.
Dedupe cache evicts too eagerly
An in-memory dedupe cache drops the earliest events from a burst before their retries arrive, so legitimate events get processed twice.
Dedupe stuck on failure
The dedupe marker is written before the side effect runs and never rolled back on failure, so a transient failure permanently suppresses every retry of the same event.
Dedupe window too short
Webhook dedupe cache drops entries long before providers stop retrying, so duplicates get processed twice.
Deploy Argv Offset
The deploy script reads the user flags from the wrong argv indices.
DNS cache ignores TTL
Cached DNS answers are never re-validated, so record rotations are missed.
Dotenv Empty String Truthy
An unset env var with an empty default still turns a maintenance flag on.
Duplicate webhook processing
Each request registers another event listener, so later webhooks are processed multiple times.
Env Merge Drops Nested Keys
An env override on a nested config silently wipes sibling keys set by the base.
Event id mutated by middleware
The dedupe fingerprint includes middleware-added fields, so the same webhook event is treated as a brand new event on every retry.
Failover Still Routes to Primary
A healthy secondary region exists, but delivery remains pinned to the unhealthy primary.
forEach With an Async Callback
Array.prototype.forEach does not await async callbacks, so the batch reports success while per-item work is still running.
Frozen Config At Import Time
Test-time env overrides never reach the running config because the snapshot was captured at import.
Half an import
A rejected batch still leaves earlier rows committed.
Healthcheck Env Shadow
Process env wins over the option object the caller hands to the healthcheck.
HTTP method casing mishandled for non-standard verbs
A Request constructor accepts a method option. The library normalizes the six standard HTTP methods (DELETE, GET, HEAD, OPTIONS, POST, PUT) to uppercase regardless of input casing, while preserving every other method string exactly as supplied. Currently, the casing rule applies too broadly, altering method names that should pass through untouched.
Idempotency fingerprint key-order sensitive
The dedupe fingerprint uses naive JSON.stringify, so identical payloads with reordered object keys are treated as new requests.
Idempotency key ignored
The payment endpoint validates an idempotency key but drops it before the database call.
Idempotency key trimmed too much
Retries with the same Idempotency-Key header are treated as new requests because the writer and reader disagree on the key shape.
Inner join drops comments from soft-deleted authors
A LEFT JOIN needs an INNER JOIN — or vice versa — and rows silently disappear from the listing.
Jitter Can Go Negative
Subtractive jitter helper produces negative delays when the jitter range is wider than the base delay.
Lock Around the Wrong Axis
Reservations for distinct slots owned by the same caller serialize unnecessarily.
Loop Mutates the Source Array
processJobs splices skipped jobs out of the input array while iterating, so the iterator drifts past subsequent entries.
Lowercase header lookup
Header lookup fails whenever the casing differs from what the caller passes in.
LRU evicts the wrong tenant
An LRU cache evicts the most-recently-used tenant instead of the least-recently-used one.
Memoization keyed on object identity
A memoized helper uses the query object as the Map key, so logically equal queries always miss the cache.
Migrate Skips On Equal Version
A migration that was never applied keeps being skipped because the runner compares strings naively.
Multi-tenant cache key prefix missing
A cache key utility drops the tenant id, so two tenants with the same resource id share one cached value.
N+1 that silently drops rows
A per-row fetch helper filters on a property that does not exist, so every caller sees an empty result.
Negative cache poisoning
A null result is cached so aggressively that a freshly created record never becomes visible.
No content with body
DELETE returns 204 alongside a payload, which strict clients reject.
Null bypasses cache validation
A short-circuit on falsy role keys returns a hardcoded default and skips the cache entirely.
Pagination filter applied after the slice
The query builder paginates first and filters second, so filtered pages are short and skip matching rows.
Pagination off by one
Page 1 skips the first N items.
Pagination skips rows on timestamp ties
Cursor-based pagination drops every row whose updated_at equals the boundary timestamp on the previous page.
Pagination: total counted after slicing
The list endpoint reports a `total` of page-size, so clients stop paginating after page 1.
Partial Success Discarded by Batch
Batch processor throws away every successful item whenever any sibling fails.
Payment dedupe ignores amount mismatch
A retry that changes the payment amount under the same idempotency key silently returns the original response instead of raising.
Permit Released Twice on Success
withLock releases the permit in both the happy path and finally, so the semaphore silently over-credits.
Port Resolves From CWD
Config file silently disappears when the binary is launched from a different directory.
Preflight omits Access-Control-Expose-Headers
Custom response headers are invisible to cross-origin callers because the preflight never declares them.
Prod Flag Defaulted On
A feature flag set to 'false' still ships as enabled in production.
Pseudo-unique constraint leaks duplicate accounts
An index assumed unique is queried by the wrong column, so duplicates slip through signup.
Rate-limit bucket shared across users
The rate-limit key omits the user id, so every caller shares one bucket per route.
Rate-limit window never resets
A fixed-window limiter locks a tenant out forever after their first window fills.
Redirect drops authorization
Authorization header is stripped when following a 3xx redirect.
Region Fallback Shadow
A baked-in region in the config file silently overrides the orchestrator's region.
Registry Reset Forgets Half the State
resetRegistry() zeros the id counter but leaves the items array populated, so the next test starts with stale entries.
Rejection Escapes Several Call Hops
prepare() drops the await on its inner load() call, so the rejection escapes past try/catch and runJob reports success.
Repeated trailing slashes in base URL
When a base URL like 'https://api.github.com///' is joined with a path like '/users', the resulting URL contains an extra slash between the host and the path instead of a single separator.
Retry After Shutdown Stalls Quietly
Shutdown waits for the retry queue to drain instead of cancelling in-flight loops, and the queued promise rejects with the wrong error.
Retry Budget Shared Across Callers
Consumed retry budget slots are never refunded, so successful callers permanently deplete the budget for concurrent callers.
Retry regenerates idempotency key
The retry wrapper around an outbound call generates a fresh idempotency key on every attempt, so the destination service treats each retry as a brand new request.
Silent failure as 200
Transport errors are swallowed and surfaced as HTTP 200 OK.
Socket reused after error
A socket that errored mid-response is returned to the pool and serves stale bytes to the next request.
Spy Counter Survives Reset
resetSpy clears the call log but leaves the invocation counter, so the next test sees a count that disagrees with the log.
Stale-while-revalidate serves stale forever
The revalidation branch never calls the fetcher, so cached entries become permanent.
Status before error check
API gateway returns HTTP 200 to clients that should receive a 4xx error.
Tenant id bleeds through async fetch
An async enrichment step uses a hard-coded tenant label, so every tenant receives the same enriched profile.
Tenant prefix skipped on read
Writes namespace cache entries by tenant but reads ignore the namespace, so every tenant reads the same entry.
The Artifact That Belonged To Yesterday
uploadArtifact returns the URL of the first run for every subsequent runId.
The Broken Health Check
Deployment rolls back even though app is alive.
The Case-Sensitive Config
Works on macOS, fails in Linux CI.
The Config That Swallowed Prod
Production boots with the dev database URL and debug flags.
The Constant-Time Compare That Compounded The Opposite
HMAC verify path accepts forged signatures and rejects genuine ones — the bitwise compare is inverted.
The Counter That Forgot
Two concurrent increments on the same key lose one of the updates.
The Crossed Wires
Two concurrent moves between the same accounts deadlock because the lock order is inverted.
The Dashboard That Averaged Averages
On-call swears p95 is fine. Customers still time out.
The Event That Fired Twice
Webhook side effects run twice after every reconnect.
The Failure That Disappeared In The Aggregate
A failed continuable step is recorded but the stage aggregate still reports ok:true.
The File That Left the Vault
A document download endpoint serves more than the vault folder.
The Filter That Swallowed False
?includeDeleted=false still returns soft-deleted rows.
The Flaky Retry
Retry logic makes the outage worse.
The Header That Vanished Mid-Flight
Authenticated upstream calls 401 even though the client set Authorization.
The healthy secondary no one routes to
The primary is unhealthy, but stale routing state keeps sending traffic there while a healthy secondary waits idle.
The Invoice That Forgot Quantity
Carts with quantity > 1 undercharge. Finance finds missing line totals.
The Invoice That Stuck Around For An Extra Month
addMonths advances every invoice by one month past the intended date.
The job that two workers both completed
A queue retry races with another worker and applies the same side effect twice.
The Lock That Left Too Soon
A writer's lock is dropped before its writes are applied; a second transaction observes stale data.
The Lock That Never Let Go
After one failing job, every later attempt waits forever on the same lock.
The log that ate the request
A debug log serializes an entire upload payload.
The Missing Header
API works in browser, fails in worker/client.
The missing trace ID
A downstream request loses its correlation header.
The Month That Was Always One Ahead
Every month on the dashboard is labelled with the next month's name.
The N+1 That Wasn't
DB load explodes, but the query count looks normal.
The Order That Stayed Open
An order is recorded even though one of its inventory deductions failed.
The order total from the other region
A multi-tenant order API returns a cached order from the wrong region after a traffic burst.
The Payment Key That Ended Up In The Logs
PaymentError's message embeds the request, including the Authorization header that holds the API key.
The payment that charged twice after a timeout
A timed-out payment is retried, but the provider records two charges for one order.
The Permission That Outlived the User
Security revokes a contractor. API logs still show successful deletes.
The Phantom Env Var
Works locally. Fails in CI after a config change.
The Phantom Read
A read inside a transaction does not observe the same transaction's pending write.
The Pipeline Stage That Always Won
runStage marks failed stages as succeeded, silently swallowing runner errors.
The pool that never gives connections back
A read API works locally, then queues and returns 5xx under sustained traffic as the database pool drains.
The PR Build That Held A Production Key
getDeploySecret returns the production credential on PR builds from forks because the branch-gate is missing.
The Price That Refused to Update
Merchants change a price. Half the storefront still shows yesterday's number.
The Query That Loaded the World
Orders list endpoint returns every tenant's rows and times out under load.
The Rate Limit That Counted Everyone
One noisy tenant trips the rate limit for every customer on the node.
The Redirect That Left the Building
Login next= parameter sends users to attacker-controlled hosts.
The Retry That Amplified Outages
When auth fails, clients hammer it harder. Traffic multiplies the outage.
The Retry That Charged Twice
A retry loop sends each attempt with a new idempotency key, so a transient failure produces a duplicate charge.
The Silent Timeout
Requests hang for 30 seconds, then succeed on retry.
The stale confirmation
A successful update is followed by an old value.
The Stale Lock
Deploy pipeline stuck because an old lock never expired.
The Timezone Trap
Report fails only around midnight UTC.
The Token That Never ExpireChecked
JWT verify path accepts expired tokens and rejects unexpired ones because it reads the wrong claim.
The Token That Never Expired
Revoked sessions still authorize long after their expiresAt.
The token that only fails after deploy
Valid API tokens work locally but production rejects them because configuration precedence selects the wrong audience.
The Update That Forgot Its Pin
An optimistic update accepts an expectedVersion but never compares it; concurrent writes silently overwrite each other.
The Wallet That Let It Slide
Two concurrent transfers from the same account both succeed and the balance goes negative.
The Webhook That Trusted Silence
Unsigned webhook deliveries are accepted when the signature header is missing.
The Week That Started A Day Late
Weekly buckets drop Sunday events into the current week and weekday events into the previous one.
Timeout Not Cleared on Success
Request helper returns successfully but leaves its deadline timer armed, causing later requests to be aborted by stale timers.
Token bucket refills on every request
Each call resets the bucket to capacity, so the limiter never throttles.
Token bucket skips the ms→seconds conversion
`msToSeconds` returns milliseconds unchanged, so the bucket refills thousands of times faster than configured and any short pause is treated as a long one.
TTL refreshed on every read
A read-time TTL refresh keeps hot entries alive indefinitely and stale data never evicts.
Two For One Email
Concurrent registrations for the same email both succeed and create duplicate accounts.
Undrained connection pool
Connection pool grows past its declared capacity because releases are never throttled.
Unencoded query params
URL builder concatenates raw values, corrupting queries that contain spaces or reserved characters.
Webhook signature tolerance too wide
The signature timestamp tolerance is measured in days, so a captured webhook can be replayed hours later.
Wrong Port, Right Service
Health check passes, traffic hits the admin service instead of the public API.
Zero-value entries-read argument omitted from XGROUP tokens
A caller passes the ENTRIESREAD option set to 0 to XGROUP CREATE or XGROUP SETID. The argument builder should emit the ENTRIESREAD keyword and a '0' token. Instead, when the option value is exactly 0, the trailing ENTRIESREAD and '0' tokens are missing from the produced array.