Node.js & Backend JS debugging
Server-side JavaScript debugging — event-loop lag, stream backpressure, ESM/CommonJS interop, Express/Fastify middleware, and the runtime errors that only show up under load.
41 guides·136 playable labs
Playable labs
The Phantom Env VarWorks locally. Fails in CI after a config change.Config & Environment · EasyWrong Port, Right ServiceHealth check passes, traffic hits the admin service instead of the public API.Network & Connectivity · EasyThe Stale LockDeploy pipeline stuck because an old lock never expired.CI/CD & Pipelines · EasyAccessDenied at 3AMProduction job fails only after rotating credentials.Secrets & Auth · MediumThe Silent TimeoutRequests hang for 30 seconds, then succeed on retry.Network & Connectivity · MediumCache StampedeEvery morning at 9AM service slows to a crawl for 90 seconds.Caching & Performance · MediumThe N+1 That Wasn'tDB load explodes, but the query count looks normal.Database · MediumThe Missing HeaderAPI works in browser, fails in worker/client.HTTP/API · EasyThe Flaky RetryRetry logic makes the outage worse.Reliability · MediumThe Broken Health CheckDeployment rolls back even though app is alive.Deployment · EasyThe Timezone TrapReport fails only around midnight UTC.Date/Time · MediumThe Case-Sensitive ConfigWorks on macOS, fails in Linux CI.Config & Environment · EasyPagination off by onePage 1 skips the first N items.HTTP/API · EasyCORS origin parsing bugRegex origin validation allows malicious domains.Security · MediumCache key missing tenant idMulti-tenant cache key uses just user_id, causing cross-tenant leaks.Caching & Performance · HardThe stale confirmationA successful update is followed by an old value.Databases & Consistency · MediumThe missing trace IDA downstream request loses its correlation header.Observability · EasyHalf an importA rejected batch still leaves earlier rows committed.Data Integrity · HardThe log that ate the requestA debug log serializes an entire upload payload.Performance & Observability · MediumThe Price That Refused to UpdateMerchants change a price. Half the storefront still shows yesterday's number.Caching & Performance · EasyThe Invoice That Forgot QuantityCarts with quantity > 1 undercharge. Finance finds missing line totals.Data Integrity · MediumThe Retry That Amplified OutagesWhen auth fails, clients hammer it harder. Traffic multiplies the outage.Reliability · EasyThe Header That Vanished Mid-FlightAuthenticated upstream calls 401 even though the client set Authorization.Network & Connectivity · MediumThe Query That Loaded the WorldOrders list endpoint returns every tenant's rows and times out under load.Database · MediumThe Filter That Swallowed False?includeDeleted=false still returns soft-deleted rows.HTTP/API · MediumThe Rate Limit That Counted EveryoneOne noisy tenant trips the rate limit for every customer on the node.Reliability · MediumDedupe window too shortWebhook dedupe cache drops entries long before providers stop retrying, so duplicates get processed twice.Events & Concurrency · EasyIdempotency key trimmed too muchRetries with the same Idempotency-Key header are treated as new requests because the writer and reader disagree on the key shape.API & Request Flow · EasyEvent id mutated by middlewareThe dedupe fingerprint includes middleware-added fields, so the same webhook event is treated as a brand new event on every retry.Events & Concurrency · EasyPayment dedupe ignores amount mismatchA retry that changes the payment amount under the same idempotency key silently returns the original response instead of raising.Data Integrity · MediumDedupe cache evicts too eagerlyAn in-memory dedupe cache drops the earliest events from a burst before their retries arrive, so legitimate events get processed twice.Reliability · MediumWebhook signature tolerance too wideThe signature timestamp tolerance is measured in days, so a captured webhook can be replayed hours later.HTTP/API · MediumIdempotency fingerprint key-order sensitiveThe dedupe fingerprint uses naive JSON.stringify, so identical payloads with reordered object keys are treated as new requests.Data Integrity · MediumAsync dedupe raceTwo 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.Events & Concurrency · HardRetry regenerates idempotency keyThe 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.API & Request Flow · HardDedupe stuck on failureThe 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.Events & Concurrency · HardAttempt Counter Reset Mid-LoopRetry helper never reaches its attempt budget because the counter is reset between tries.Reliability · EasyTimeout Not Cleared on SuccessRequest helper returns successfully but leaves its deadline timer armed, causing later requests to be aborted by stale timers.Reliability · EasyPartial Success Discarded by BatchBatch processor throws away every successful item whenever any sibling fails.Reliability · EasyBackoff Overflow at High AttemptsExponential backoff returns Infinity once the attempt count gets large enough to overflow the exponent.Reliability · MediumJitter Can Go NegativeSubtractive jitter helper produces negative delays when the jitter range is wider than the base delay.Reliability · MediumRetry Budget Shared Across CallersConsumed retry budget slots are never refunded, so successful callers permanently deplete the budget for concurrent callers.Reliability · MediumCircuit Half-Open Floods RequestsCircuit breaker admits every incoming request while half-open instead of a single probe, hammering the recovering downstream.Reliability · MediumDeadline Truncated SilentlyDeadline-aware helper returns the partial result instead of rejecting when the injected clock advances past the deadline.Reliability · HardRetry After Shutdown Stalls QuietlyShutdown waits for the retry queue to drain instead of cancelling in-flight loops, and the queued promise rejects with the wrong error.Reliability · HardBackoff Jitter RNG Reset Per AttemptPRNG factory is invoked per attempt, so every jittered backoff collapses to the same first draw.Reliability · HardCache TTL: ms vs secondsIn-memory cache treats milliseconds as seconds on read, so every entry expires on the next call.Caching & Performance · EasyCache mutation leaks across tenantsCached objects are handed out by reference; one tenant's mutation rewrites what other tenants read.Caching & Performance · EasyNegative cache poisoningA null result is cached so aggressively that a freshly created record never becomes visible.Caching & Performance · MediumStale-while-revalidate serves stale foreverThe revalidation branch never calls the fetcher, so cached entries become permanent.Caching & Performance · MediumLRU evicts the wrong tenantAn LRU cache evicts the most-recently-used tenant instead of the least-recently-used one.Caching & Performance · MediumTenant prefix skipped on readWrites namespace cache entries by tenant but reads ignore the namespace, so every tenant reads the same entry.Caching & Performance · MediumCache invalidation skipped on writeWrites update the database but the cache is never told, so reads keep returning the old value.Caching & Performance · HardTenant id bleeds through async fetchAn async enrichment step uses a hard-coded tenant label, so every tenant receives the same enriched profile.Caching & Performance · HardMulti-tenant cache key prefix missingA cache key utility drops the tenant id, so two tenants with the same resource id share one cached value.Caching & Performance · HardCache key delimiter collisionTwo cache lookups that should be distinct reduce to the same string and overwrite each other.Caching & Performance · EasyThe Counter That ForgotTwo concurrent increments on the same key lose one of the updates.Data Integrity · EasyThe Lock That Left Too SoonA writer's lock is dropped before its writes are applied; a second transaction observes stale data.Reliability · EasyThe Wallet That Let It SlideTwo concurrent transfers from the same account both succeed and the balance goes negative.Databases & Consistency · EasyTwo For One EmailConcurrent registrations for the same email both succeed and create duplicate accounts.Databases & Consistency · MediumLock Around the Wrong AxisReservations for distinct slots owned by the same caller serialize unnecessarily.Events & Concurrency · MediumThe Order That Stayed OpenAn order is recorded even though one of its inventory deductions failed.Reliability · MediumThe Phantom ReadA read inside a transaction does not observe the same transaction's pending write.Databases & Consistency · MediumThe Update That Forgot Its PinAn optimistic update accepts an expectedVersion but never compares it; concurrent writes silently overwrite each other.Data Integrity · HardThe Crossed WiresTwo concurrent moves between the same accounts deadlock because the lock order is inverted.Events & Concurrency · HardThe Retry That Charged TwiceA retry loop sends each attempt with a new idempotency key, so a transient failure produces a duplicate charge.Reliability · HardRate-limit window never resetsA fixed-window limiter locks a tenant out forever after their first window fills.Reliability · EasyPagination: total counted after slicingThe list endpoint reports a `total` of page-size, so clients stop paginating after page 1.HTTP/API · EasyCORS startsWith() allows lookalike originsCORS allowlist uses `String.prototype.startsWith`, so any domain beginning with our trusted host passes.Security · EasyToken bucket refills on every requestEach call resets the bucket to capacity, so the limiter never throttles.Reliability · MediumPreflight omits Access-Control-Expose-HeadersCustom response headers are invisible to cross-origin callers because the preflight never declares them.API & Request Flow · MediumRate-limit bucket shared across usersThe rate-limit key omits the user id, so every caller shares one bucket per route.Reliability · MediumCursor built from a non-unique columnPagination cursor encodes only `status`, causing rows with duplicate statuses to be skipped or repeated.HTTP/API · MediumPagination filter applied after the sliceThe query builder paginates first and filters second, so filtered pages are short and skip matching rows.Data Integrity · HardCORS allows the null origin unconditionallyCORS echoes Access-Control-Allow-Origin: null when the request has no Origin header, exposing the API to sandboxed iframes and file:// pages.Security · HardToken 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.Data Integrity · HardProd Flag Defaulted OnA feature flag set to 'false' still ships as enabled in production.Config & Environment · EasyPort Resolves From CWDConfig file silently disappears when the binary is launched from a different directory.Config & Environment · EasyDotenv Empty String TruthyAn unset env var with an empty default still turns a maintenance flag on.Config & Environment · MediumFrozen Config At Import TimeTest-time env overrides never reach the running config because the snapshot was captured at import.Config & Environment · MediumEnv Merge Drops Nested KeysAn env override on a nested config silently wipes sibling keys set by the base.Config & Environment · HardHealthcheck Env ShadowProcess env wins over the option object the caller hands to the healthcheck.Deployment · EasyDeploy Argv OffsetThe deploy script reads the user flags from the wrong argv indices.Deployment · EasyRegion Fallback ShadowA baked-in region in the config file silently overrides the orchestrator's region.Deployment · MediumMigrate Skips On Equal VersionA migration that was never applied keeps being skipped because the runner compares strings naively.Deployment · MediumAsset Cache Bust On Stale RevStatic asset URLs ship with the dev revision because the build id was captured at import time.Deployment · HardStatus before error checkAPI gateway returns HTTP 200 to clients that should receive a 4xx error.HTTP/API · EasyNo content with bodyDELETE returns 204 alongside a payload, which strict clients reject.HTTP/API · EasyLowercase header lookupHeader lookup fails whenever the casing differs from what the caller passes in.HTTP/API · MediumUnencoded query paramsURL builder concatenates raw values, corrupting queries that contain spaces or reserved characters.HTTP/API · MediumSilent failure as 200Transport errors are swallowed and surfaced as HTTP 200 OK.HTTP/API · HardRedirect drops authorizationAuthorization header is stripped when following a 3xx redirect.Network & Connectivity · EasyContent-Type charset lieBody decoder ignores the declared charset and always assumes UTF-8.Network & Connectivity · MediumDNS cache ignores TTLCached DNS answers are never re-validated, so record rotations are missed.Network & Connectivity · MediumUndrained connection poolConnection pool grows past its declared capacity because releases are never throttled.Network & Connectivity · HardSocket reused after errorA socket that errored mid-response is returned to the pool and serves stale bytes to the next request.Network & Connectivity · HardInner join drops comments from soft-deleted authorsA LEFT JOIN needs an INNER JOIN — or vice versa — and rows silently disappear from the listing.Database · EasyCount that missed the where clauseA dashboard count helper ignores its filter and reports the wrong total.Database · EasyN+1 that silently drops rowsA per-row fetch helper filters on a property that does not exist, so every caller sees an empty result.Database · MediumPagination skips rows on timestamp tiesCursor-based pagination drops every row whose updated_at equals the boundary timestamp on the previous page.Database · MediumPseudo-unique constraint leaks duplicate accountsAn index assumed unique is queried by the wrong column, so duplicates slip through signup.Database · HardCache invalidated before the write commitsUpdate helper primes the cache with the pre-write snapshot, so readers see the old value after every update.Caching & Performance · EasyMemoization keyed on object identityA memoized helper uses the query object as the Map key, so logically equal queries always miss the cache.Caching & Performance · MediumTTL refreshed on every readA read-time TTL refresh keeps hot entries alive indefinitely and stale data never evicts.Caching & Performance · MediumCache stampede without coalescingConcurrent cold-cache callers each kick off their own expensive compute, overwhelming the worker.Caching & Performance · HardNull bypasses cache validationA short-circuit on falsy role keys returns a hardcoded default and skips the cache entirely.Caching & Performance · HardAwait Missing in Fire-and-ForgetBatch runner swallows async failures because the per-item call is not awaited.Events & Concurrency · EasyforEach With an Async CallbackArray.prototype.forEach does not await async callbacks, so the batch reports success while per-item work is still running.Events & Concurrency · EasyLoop Mutates the Source ArrayprocessJobs splices skipped jobs out of the input array while iterating, so the iterator drifts past subsequent entries.Events & Concurrency · MediumPermit Released Twice on SuccesswithLock releases the permit in both the happy path and finally, so the semaphore silently over-credits.Events & Concurrency · MediumRejection Escapes Several Call Hopsprepare() drops the await on its inner load() call, so the rejection escapes past try/catch and runJob reports success.Events & Concurrency · HardRegistry Reset Forgets Half the StateresetRegistry() zeros the id counter but leaves the items array populated, so the next test starts with stale entries.Reliability · EasySpy Counter Survives ResetresetSpy clears the call log but leaves the invocation counter, so the next test sees a count that disagrees with the log.Reliability · MediumThe Month That Was Always One AheadEvery month on the dashboard is labelled with the next month's name.Date/Time · EasyThe Week That Started A Day LateWeekly buckets drop Sunday events into the current week and weekday events into the previous one.Date/Time · MediumThe Invoice That Stuck Around For An Extra MonthaddMonths advances every invoice by one month past the intended date.Date/Time · HardThe Token That Never ExpireCheckedJWT verify path accepts expired tokens and rejects unexpired ones because it reads the wrong claim.Secrets & Auth · EasyThe Payment Key That Ended Up In The LogsPaymentError's message embeds the request, including the Authorization header that holds the API key.Secrets & Auth · MediumThe Constant-Time Compare That Compounded The OppositeHMAC verify path accepts forged signatures and rejects genuine ones — the bitwise compare is inverted.Secrets & Auth · HardThe Pipeline Stage That Always WonrunStage marks failed stages as succeeded, silently swallowing runner errors.CI/CD & Pipelines · EasyThe Artifact That Belonged To YesterdayuploadArtifact returns the URL of the first run for every subsequent runId.CI/CD & Pipelines · MediumThe PR Build That Held A Production KeygetDeploySecret returns the production credential on PR builds from forks because the branch-gate is missing.CI/CD & Pipelines · MediumThe Failure That Disappeared In The AggregateA failed continuable step is recorded but the stage aggregate still reports ok:true.CI/CD & Pipelines · HardFailover Still Routes to PrimaryA healthy secondary region exists, but delivery remains pinned to the unhealthy primary.Reliability · MediumThe pool that never gives connections backA read API works locally, then queues and returns 5xx under sustained traffic as the database pool drains.Data & Reliability · HardThe payment that charged twice after a timeoutA timed-out payment is retried, but the provider records two charges for one order.Payments & Reliability · HardDuplicate webhook processingEach request registers another event listener, so later webhooks are processed multiple times.Events & Concurrency · MediumThe order total from the other regionA multi-tenant order API returns a cached order from the wrong region after a traffic burst.Caching & State · HardThe healthy secondary no one routes toThe primary is unhealthy, but stale routing state keeps sending traffic there while a healthy secondary waits idle.Failover & Routing · HardHTTP method casing mishandled for non-standard verbsA 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.API & Request Flow · EasyRepeated trailing slashes in base URLWhen 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.API & Request Flow · EasyZero-value entries-read argument omitted from XGROUP tokensA 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.Data & Serialization · MediumApp.render rejects null and undefined options valuesA 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.Config & Environment · EasyIdempotency key ignoredThe payment endpoint validates an idempotency key but drops it before the database call.API & Request Flow · MediumThe job that two workers both completedA queue retry races with another worker and applies the same side effect twice.Queues & Concurrency · HardThe token that only fails after deployValid API tokens work locally but production rejects them because configuration precedence selects the wrong audience.Auth & Configuration · Hard
Debugging guides
API returns 404 after deploy: how to debug itAPI/backend debugging · BeginnerTimezone off by one day in JavaScript: how to debug itJavaScript/Node runtime debugging · IntermediateSlow API response: how to debug latency issuesAPI/backend debugging · IntermediateN+1 query slowing down your API: how to find and fix itAPI/backend debugging · IntermediateWorker process memory leak: how to find and fix memory leaks in background jobsJavaScript/Node runtime debugging · AdvancedRequest body undefined in Express: how to debug missing request bodiesJavaScript/Node runtime debugging · BeginnerESM CommonJS import error: how to debug module system conflictsJavaScript/Node runtime debugging · IntermediateAPI timeout: how to debug request timeout issuesAPI/backend debugging · IntermediateStale lock bug: how locks that are never refreshed cause outagesJavaScript/Node runtime debugging · AdvancedHidden state bug in React form: how to debug stale closures and state issuesJavaScript/Node runtime debugging · IntermediateDebugging UnhandledPromiseRejection in Node.js ApplicationsJavaScript · IntermediateDiagnosing Node.js Event Loop Lag and High Latency in ProductionNode.js · AdvancedExpress Middleware Not Executing in Order: A Debugging GuideNode.js · BeginnerNode.js ESM/CommonJS Interop: require() of ES Module FailsNode.js · IntermediateDebugging Node.js Stream Backpressure: When HighWaterMark Betrays YouNode.js · AdvancedFastify Plugin Not Registered: Debugging the 'Missing Plugin' ErrorNode.js · IntermediateNode.js Graceful Shutdown Not Completing: Debugging Dirty Handles and Stuck TimersNode.js · IntermediateExpress-Session Not Saving Between Requests: The Real CausesNode.js · IntermediateNode.js Module Resolution: Why 'Cannot Find Module' Keeps HappeningNode.js · IntermediateExpress req.body Undefined: Body-Parser Not WorkingNode.js · BeginnerDebug Node.js child_process.spawn() FailuresNode.js · IntermediateNode.js Worker Threads Messages Not Received: Debugging Communication FailuresNode.js · AdvancedNode.js Crypto Module Error: Common Failures and FixesNode.js · IntermediateNode.js Buffer Encoding Decoding Errors: A Production Debugging GuideNode.js · IntermediateMulter File Upload Not Working in Express: A Debugging GuideNode.js · IntermediateNode.js Inspector Debugger: Diagnosing Protocol Disconnections and Async BreakpointsNode.js · IntermediateDebugging Node.js Cluster Module Worker CrashesNode.js · AdvancedJavaScript Closure Stale Variable Inside LoopJavaScript · IntermediateDebugging Prototype Chain Inheritance Bugs in JavaScriptJavaScript · AdvancedJavaScript Microtask & Promise Execution Order DebuggingJavaScript · AdvancedJavaScript Generator Iterator Not Working: Debugging Silent FailuresJavaScript · AdvancedDebugging for-await-of Errors with Async IteratorsJavaScript · AdvancedDebugging Unexpected State Changes from JavaScript Object MutationJavaScript · IntermediateDebugging JavaScript Proxy Reflect Traps: When Your Proxy Silently FailsJavaScript · AdvancedDebugging 'this' Being Undefined in JavaScriptJavaScript · IntermediateJavaScript Regex Catastrophic Backtracking Debug GuideJavaScript · AdvancedDebugging JavaScript NaN Comparison: Why NaN === NaN is FalseJavaScript · BeginnerWhy Optional Chaining Returns Unexpected UndefinedJavaScript · BeginnerBun Runtime Compatibility Error: A Debugging GuideNode.js · IntermediateDeno Permission Denied Error: A Practical Debugging GuideNode.js · IntermediateElectron IPC Breakdown: When main and renderer stop talkingJavaScript · Advanced