AI Joe
← Blog· AI Axiom

The Async Bug That Hides in Plain Sight When AI Writes Your Callbacks

September 23, 2026

A closure that looks correct on first read can still capture the wrong value, and AI models write these closures fast enough that nobody stops to check the timing.

The Details:

  • Loop variables get shared across callbacks when a model extends existing async code by matching its style. Each iteration registers a handler that references the same variable, so when the callbacks finally fire, they all read the final value instead of the one present at creation time. The code compiles and passes a quick skim, which is exactly why it survives review.

  • Event listeners that never get removed keep a reference to state long after the component that created them is gone. Memory climbs, behavior turns strange, and none of it points cleanly back to the missing teardown. Tests rarely simulate the real timing gap between when a handler captures state and when it reads it, so green CI gives false confidence.

  • Mixed async idioms slip through unnoticed: a promise chain that accepts a callback, an async function missing an await inside a loop. These read as coherent single-file output, but the failure only shows up under load or on an edge case weeks after launch.

  • Four questions catch most of this before it ships: what state does the handler capture at creation, what does it read when it fires, what happens if it fires twice, and what happens if it never fires. Applying these to one boring piece of async code, like a retry handler, builds the review habit faster than auditing the most complex pipeline in the codebase.

Bottom Line: Where state lives in an async system is an architectural decision, and if nobody makes it on purpose, the model makes it by default.

Enjoy this article?

Listen to the Claude Code Conversations radio show or join the community.