1. Introduction
a coding agent in motion, you’ve most likely seen the default workflow: let the AI deal with the routine bugs, however step in when the issue will get difficult. One-line fixes go to the agent; deep internals, numerical edge instances, and cross-file invariants stick with the senior engineer.
That made me marvel: what sorts of bugs do coding brokers really battle with?
Over the previous month, I ran 28 blind-scored debugging experiments on three actual, lately mounted bugs from manufacturing open-source libraries: ky, immer, and decimal.js.
Sarcastically, the 2 bugs I anticipated to be the toughest turned out to not be an issue in any respect. One was buried deep inside Immer’s internals, and the opposite was a refined numerical edge case in decimal.js. Throughout 16 makes an attempt, the AI mounted each appropriately each single time.

The third bug appeared nearly trivial: an HTTP shopper silently dropped a retry choice. But it defeated each mannequin and workflow I examined.
Throughout all 12 runs, the AI produced a “repair” that truly corrupts person knowledge. By any cheap normal, none of these runs had been profitable.
What issues me isn’t simply that the fixes had been incorrect—it’s that each single one handed your entire 84-test retry suite for the code underneath restore.
That’s the true failure mode. In case your staff merges AI-generated fixes as a result of CI is inexperienced, that is precisely the form of bug that slips by means of: it appears to be like easy, the checks cross, and the implementation quietly corrupts person knowledge when it collides with Ky’s present choice form.
Right here’s what these 28 runs revealed—and why I believe they’re related to anybody constructing or deploying coding brokers.
Should you’re an engineer who depends on AI to write down manufacturing code, a tech lead deciding the place AI could be trusted, or a researcher fascinated with the true limits of code technology, these outcomes spotlight a failure mode that isn’t apparent from benchmark scores. The query isn’t whether or not AI can clear up arduous bugs. It’s whether or not it is aware of when it doesn’t have sufficient data to unravel a straightforward one.
- Issue didn’t predict failure—lacking data did. When the proper repair may very well be inferred from the codebase and the bug report, the AI succeeded in all 16 makes an attempt, together with bugs buried in unfamiliar proxy internals and refined numerical edge instances. However when the proper repair trusted an undocumented API contract, it failed in all 12 makes an attempt—throughout Claude Haiku 4.5, Sonnet 5, and Opus 4.8, utilizing three totally different agent workflows.
- Extra course of didn’t repair the issue. In a single experiment, a reviewer agent appropriately recognized that the proposed patch would corrupt person knowledge and defined precisely why. It then authorised the change anyway, reasoning that the problem was unlikely and belonged to a pre-existing class of issues. The failure wasn’t in detection—it was in judgment. Even when the system acknowledged the chance, it lacked the decision-making wanted to cease the unhealthy repair from delivery.
2. Let’s begin with the bugs that I examined
To carry out this check, I choose actual bugs from actual codebases, with actual floor reality: the maintainer’s merged repair and the regression checks that shipped with it.
Three choice guidelines did the heavy lifting.
First, each bug was mounted upstream in July 2026, and it’s extremely possible that the coaching cutoff for the most recent Claude fashions (the 5 household) is earlier than this era, so no mannequin has seen the repair. Second, every repair shipped with regression checks, held out as a hidden grader the agent by no means sees. Third, I selected the three bugs to span issue, from a one-line repair to a two-file invariant restore.
| Bug | Library | What breaks | Straightforward or Arduous | Right repair |
| ky #867 | HTTP shopper constructed on fetch | numeric retry restrict silently misplaced on .lengthen() | appears to be like straightforward: one merge rule | broaden the shorthand solely on the choices root |
| immer #1255 | immutability layer behind Redux Toolkit | authentic state mutated after reverse() / kind() |
two recordsdata of proxy internals | re-draft parts the reorder relocated |
| decimal.js #260 | arbitrary-precision arithmetic | asin() returns incorrect digits close to x = 1 |
numerical evaluation: catastrophic cancellation | reformulate 1 − x² as (1 − x)(1 + x) |
2.1 The straightforward one: ky, PR #867
ky is a small HTTP shopper constructed by Sindre Sorhus. It wraps the browser’s fetch and provides the issues each app finally ends up needing anyway: retries, timeouts, JSON dealing with. The traditional method to make use of it’s to construct one base shopper along with your shared settings, then specialize it per characteristic:
const api = ky.create({retry: 3}); // one shopper, retry as much as 3 occasions
const customers = api.lengthen({retry: {strategies: ['get']}}); // similar shopper, however solely retry GETs
retry: 3 is shorthand for “retry failed requests as much as 3 occasions.”
The bug: when the bottom shopper units retry as a quantity, and the extension units it as an object, the quantity silently vanishes. The prolonged shopper quietly falls again to the default of two retries. Nothing crashes. No warning. Your requests simply retry fewer occasions than you configured, which is precisely the form of bug no person notices till a nasty community day in manufacturing.
That is the ticket each triage information would ship straight to AI. Right here is the precise bug report I gave each agent, phrase for phrase:
Bug: retry restrict is ignored after .lengthen()
ky’s retry choice accepts a quantity as shorthand for the retry restrict (the docs say: “If retry is a quantity, it will likely be used as restrict and different defaults will stay in place”). However once I set a numeric retry on a base occasion after which lengthen it with an object, the restrict is silently misplaced and falls again to the default (2):
import ky from 'ky';
const api = ky.create({retry: 3});
const prolonged = api.lengthen({retry: {strategies: ['get']}});
// I count on `prolonged` to nonetheless retry as much as 3 occasions, solely narrowing the retriable strategies.
// As an alternative it retries with the default restrict of two.
Setting each father or mother and baby as objects works. Setting the quantity on the kid works. Solely number-on-parent + object-on-child loses the restrict. Please repair it so the restrict is preserved. You possibly can examine extra on Determine 2.

2.2 The arduous one: immer, PR #1255
Immer, the immutability engine behind Redux Toolkit, ensures that the state you cross in is by no means mutated. You edit a proxy draft, and Immer produces a new state whereas leaving the unique untouched—a key property for React.
With the non-compulsory array-methods plugin enabled, that assure broke. Calling reverse() or kind() on a draft array, then modifying a component, may mutate the caller’s authentic state.
The bug was refined. Internally, Immer tracks draft objects by their array place. However reverse() and kind() reorder parts, so an authentic object can bypass the proxy examine and be returned instantly. Subsequent writes then mutate the unique state as an alternative of the draft.
The precise bug report each agent acquired:
Bug: mutating a component after reverse()/kind() mutates the unique base state
immer guarantees by no means to switch the bottom state handed to provide. However with the array-methods plugin enabled, calling reverse() or kind() inside a recipe after which writing to a component leaks the write into the caller’s authentic array.
import {produce, enableArrayMethods, setAutoFreeze} from "immer"
enableArrayMethods()
setAutoFreeze(false)
const obj3 = {id: 3}
const base = [{id: 1}, {id: 2}, obj3]
const subsequent = produce(base, d => {
d.reverse()
d[0].id = 99
})
// BUG: base is now [{id:1},{id:2},{id:99}] and obj3 is {id:99} — the bottom was mutated.
// Anticipated: base stays [{id:1},{id:2},{id:3}] and solely `subsequent` displays the change.
The identical recipe works appropriately with out the array-methods plugin. Please repair it so the bottom is rarely mutated. Verify Determine 3 for extra particulars.

2.3 The opposite arduous one: decimal.js, PR #260
decimal.js exists as a result of extraordinary floating-point numbers can’t be trusted when precision issues—monetary calculations, scientific computing, or anyplace the final digit counts. It gives arbitrary-precision arithmetic.
Its asin (inverse sine) implementation computed 1 - x² instantly. For values of x near 1, that’s numerically unstable: subtracting two practically similar numbers causes catastrophic cancellation, the place most important digits disappear and the remaining digits are dominated by rounding error. The end result was incorrect last digits exactly within the high-precision instances customers cared about.
The attention-grabbing half was the repair. The reporter recommended growing the inner precision, however that solely strikes the failure level—the identical error reappears as x will get even nearer to 1. The actual answer was an algebraic rewrite: compute (1 - x)(1 + x) as an alternative of 1 - x². The 2 expressions are mathematically equal, however the latter avoids catastrophic cancellation and preserves precision.
The precise bug report each agent acquired:
Bug: asin() loses precision for inputs very near 1
Decimal.asin(x) returns a end result whose final digit or two is incorrect when x may be very near 1 (or -1), at greater precisions. The nearer x will get to 1, the extra the tail digits drift. Values nicely away from ±1 are effective.
const Decimal = require('./decimal.js');
Decimal.set({ precision: 30 });
// For x extraordinarily near 1, e.g. 0.99999999999999999, the ultimate digits of
// asin(x) disagree with a high-precision reference (checked towards mpmath).
console.log(new Decimal('0.99999999999999999').asin().toString());
Please make asin() correct close to ±1. Verify Determine 4 for extra particulars.

3. How I examined: fashions, workflows, and scoring
Each run adopted the identical form: one AI agent (or staff of brokers), one contemporary copy of the buggy repo, one symptom-only bug report, and a hidden grader the agent by no means sees.

3.1 The fashions
Three Claude tiers: Haiku 4.5 (the most affordable), Sonnet 5 (the center), and Opus 4.8 (essentially the most succesful). Identical bug reviews, similar repos, similar guidelines.
3.2 Three workflows
That is the place the precise prompts matter, so right here they’re.
Workflow 1, naive single agent. One agent will get the bug report above plus the repo and this instruction, verbatim:
Examine, reproduce, and repair the bug. Then reply with a report containing precisely:
1. ROOT CAUSE: what really triggered the reported conduct, and the file(s)/strains concerned.
2. DIFF: the ultimate unified diff of your change.
3. TESTS: the results of operating the check suite after your repair.
4. CONFIDENCE: low / medium / excessive that your repair is right and full, and one sentence why.
Workflow 2, gstack examine (gstack an open-source suite of opinionated AI coding workflows that turns coding brokers right into a digital software program staff for planning, constructing, reviewing, testing, and delivery software program. Supply: https://github.com/garrytan/gstack).
Identical bug report, however the agent should observe 5 specific steps and present every one. Step 3 is the one designed to catch precisely the entice on this research, quoted verbatim:
1. REPRODUCE — write a minimal repro and ensure the reported conduct.
2. ROOT CAUSE — hint the precise mechanism; identify the file and features.
3. IMPACT ENUMERATION — BEFORE writing any repair, enumerate all the pieces your supposed change will contact. Checklist each code path and enter that may attain the situation your repair keys on. Ask adversarially: what OTHER knowledge or inputs may match that situation? What may my change have an effect on that the bug report by no means talked about? Write this listing out in full.
4. IMPLEMENT — make the repair.
5. SELF-REVIEW — re-read your IMPACT ENUMERATION towards your implementation. For every merchandise, affirm the repair behaves appropriately or regulate it. Then run the checks.
Workflow 3, parallel pipeline.
4 brokers in sequence. Two unbiased diagnosers first, every instructed: “You’re a read-only DIAGNOSTIC agent… One other engineer will implement the repair out of your analysis, so be exact.” Then an implementer, given each diagnoses: “Two engineers independently identified the bug; their diagnoses are beneath. Implement the repair, utilizing their evaluation. In the event that they disagree, use your judgment.” Lastly a reviewer with the facility to alter code, instructed: “Assessment their change critically earlier than it ships — that is the final gate earlier than merge… Does the repair have any UNINTENDED unwanted effects? Assume arduous about all the pieces the modified code path touches… What may very well be affected that the unique bug report by no means talked about? … Should you discover any drawback, FIX it instantly within the code your self.”
3.3 Execution and scoring
Every run began from a contemporary checkout of the repository on the pre-fix commit, totally remoted from each different run. Brokers may run the library’s present seen checks as usually as they wished: ky’s 84-test retry suite, Immer’s base check suite, or decimal.js’s 22,624-assertion suite. What they by no means noticed had been the regression checks the maintainer added with the precise repair.
After every run, I restored the unique check recordsdata, added the held-out regression checks, ran the complete suite, and scored solely the code left on disk. The agent’s personal declare of success didn’t depend—and that distinction mattered: brokers routinely wrote checks that handed their very own fixes after which declared victory. A run was thought-about right provided that it handed the hidden checks capturing the maintainer’s last supposed conduct, not merely the reported symptom.
I ran this 28 occasions as proven within the desk beneath:

4. Outcomes
4.1 The arduous bugs accomplished 16 occasions out of 16
If uncooked algorithmic complexity had been what triggered AI debugging to fail, these had been the bugs the place I anticipated it to interrupt down.
That was my speculation going into the experiments.
The Immer bug required reconstructing a structural-sharing invariant hidden deep inside its proxy implementation—one thing that’s difficult even for an engineer aware of the codebase, not to mention a mannequin seeing it for the primary time.
The decimal.js bug appeared much more misleading. Earlier than operating the experiments, I verified that the plain repair—the one recommended within the authentic bug report, merely growing the working precision—passes all 22,624 seen assertions whereas nonetheless failing all 4 hidden edge-case checks close to the numerical boundary. In different phrases, essentially the most tempting answer seems utterly right except you perceive the underlying numerical evaluation.
As an alternative, the outcomes had been the precise reverse of what I anticipated. Throughout each workflow and two mannequin tiers, all 16 runs produced right fixes. The consistency stunned me way over the success itself.
For Immer, each run recognized the similar root trigger: the proxy assumes an array component is unchanged if it matches the component on the similar index within the authentic array. That assumption fails after reverse(), which reorders parts in place, inflicting moved objects to be returned instantly as an alternative of wrapped as drafts.
The brokers produced two distinct however right fixes. 5 of the eight runs ensured any undrafted component was wrapped as soon as the array had been reordered. The remaining three eliminated the index-based assumption fully, checking whether or not a component got here from the unique array no matter its present place.
The decimal.js outcomes had been equally notable. Not one of the eight runs took the tempting however incomplete “simply enhance the precision” strategy, although it handed the seen checks. Six of the eight runs independently rediscovered primarily the identical reformulation later merged by the maintainer, with three citing the associated acos repair (PR #217). The remaining two as an alternative made the additional precision adaptive, including extra guard digits because the enter approaches 1—another that additionally passes the hidden checks.
These experiments recommend that troublesome reasoning, unfamiliar code, and refined arithmetic weren’t the primary bottlenecks. When the knowledge wanted to derive the proper repair was already current within the codebase or bug report, the fashions persistently discovered it.

decimal.js bug is tough: the repair is algebra, no more precision. At a working precision of 20 digits, the direct formulation retains solely 3 right digits, whereas the reformulation preserves 19. Supply: Picture by writer.On bugs whose root trigger could be uncovered by studying and reasoning concerning the code, present fashions carried out higher than I anticipated. I underestimated them.
Each bugs additionally contained the clues wanted to derive the proper repair. In decimal.js, the sibling acos implementation already used the cancellation-free formulation. In Immer, the plugin units a flag indicating the array has been reordered. The mandatory data was already within the codebase, and each run discovered and used it.
That makes what occurred on the straightforward bug even stranger.
4.2. Then a one-line bug beat all the pieces
Again to Ky.
By the same old instinct about AI debugging, this could have been the simplest bug—less complicated than both Immer or decimal.js. At first, it appeared that method.
The bug was easy. If retry is first set to a quantity and later overridden with a retry object, the numeric restrict is silently misplaced. All 12 runs recognized the foundation trigger: Ky’s generic deep-merge logic doesn’t perceive its personal numeric retry shorthand. Merging { retry: 3 } with a retry object merely discards the 3.
The apparent repair was to normalize numeric values into { restrict: 3 } earlier than merging. It mounted the reported bug and handed all 84 seen retry checks.
But it surely was nonetheless incorrect.
The identical merge operate additionally processes the person’s json payload. If that payload occurs to include a retry area, the naive repair silently rewrites person knowledge, turning a quantity right into a retry configuration object. The maintainer’s repair avoids this by making use of the conversion solely to the top-level retry choice, the place retry unambiguously refers to Ky’s configuration reasonably than arbitrary person knowledge.

This can be a truthful check, not a gotcha, as a result of the pull request incorporates each variations of the repair. The contributor initially made the identical naive change. Throughout evaluation, somebody realized it may corrupt person payloads, so a second commit narrowed the repair and added a regression check to make sure nested person knowledge was by no means rewritten.
In different phrases, a human made this precise mistake in manufacturing code, and human evaluation caught it. I used that regression check as my second hidden check.
All 12 runs reproduced the unique mistake. None arrived on the reviewed repair. Each run handed the seen checks and the hidden check for the reported bug, however all 12 failed the payload-corruption check. They mounted the reported bug whereas breaking conduct that had beforehand labored. That distinction issues.
What tripped up the brokers wasn’t the code itself however a reality concerning the world outdoors it: customers can put arbitrary keys of their payloads. You possibly can infer that from the codebase—one of many twelve brokers really did—nevertheless it isn’t said within the bug report or close to the operate being mounted. Reaching the proper answer requires reasoning about how actual customers would possibly use the library, not simply the code in entrance of you.
That’s what makes the distinction with Immer and decimal.js attention-grabbing. In these instances, the knowledge wanted to unravel the bug was buried someplace within the repository, nevertheless it was discoverable—and all 16 runs discovered it.
This doesn’t appear to be an remoted sample. A latest benchmark of exact code edits discovered frontier fashions passing unit checks greater than 76% of the time whereas matching the maintainer’s edit lower than 45% of the time. My normal is definitely looser—a number of fixes I counted as right differ from the maintainer’s patch—however the hole factors to the identical challenge I noticed with ky: inexperienced checks present a repair works for the instances you examined. They don’t show the repair is definitely right.
4.3. The reviewer who discovered the bug and authorised it anyway
In a single run of the parallel pipeline, the place a reviewer agent audits the implementer’s repair earlier than it lands, the reviewer did what reviewers are for. It traced the repair and wrote out the failure exactly, in its personal phrases: “the repair keys on the string ‘retry’ at each nesting depth… deepMerge({json:{retry:3}}, {json:{retry:{foo:1}}}) → {json:{retry:{restrict:3,foo:1}}} (person request-body corruption).”
Then it authorised the merge.
Its reasoning, paraphrased: the deep-merge operate already {couples} to choice names in any respect depths, so it is a pre-existing class of drawback. A colliding secret is unlikely in apply. A clear repair wants a broader refactor. Ship it with a famous follow-up. Each sentence is defensible. I nonetheless suppose the decision is incorrect: it’s a shipped data-corruption bug {that a} maintainer rejected in the true PR.

This single run reframes your entire failure. Throughout 12 runs, the lacking contract surfaced precisely as soon as—and the method nonetheless failed. The breakdown wasn’t detection; it was the ship resolution. The reviewer weighed a reputable data-corruption discovering towards scope and “you aren’t gonna want it,” and selected to ship.
The repair isn’t a better mannequin or extra brokers. It’s a rule: any evaluation that identifies a possible unintended aspect impact or knowledge corruption blocks the merge. No weighing probability, no discretion. Underneath that rule, this failure turns into a catch. One run in twelve is sufficient—as a result of the method solely wants one reviewer to cease a nasty change. Judgment is the place the method failed; a gate removes that call.
5. Conclusion
5.1 It wasn’t the mannequin, and it wasn’t the tactic
Three apparent objections stay: possibly the fashions had been too weak, possibly a single-agent workflow was the issue, or possibly a extra rigorous evaluation course of would have caught the bug. The outcomes argue towards all three.

- mannequin energy. If this had been merely a functionality hole, the strongest fashions ought to keep away from errors the most affordable ones make. They didn’t. Claude Haiku 4.5, Sonnet 5, and Opus 4.8 all fell into the identical entice as single brokers: 7 runs, 7 corrupting fixes. The most affordable mannequin and essentially the most succesful one produced the identical failure. That stunned me greater than the Immer end result.
- workflow. I held Opus fixed and examined two extra structured processes. The primary required specific investigation: reproduce the bug, isolate the foundation trigger, enumerate potential unwanted effects (“What else may this have an effect on that the report didn’t point out?”), then implement and self-review. Three runs, three corrupting fixes.
One run got here remarkably shut. It seen {that a} nested person key named retry may incorrectly achieve a restrict, checked ky’s choice varieties, discovered no such area, and dismissed the chance. It searched the library’s vocabulary. The right reply lived in customers’ knowledge—an area no artifact ever talked about. An investigation is simply nearly as good because the assumptions that sure it.
The second workflow—a pipeline with two unbiased diagnosers, an implementer, and a reviewer empowered to rewrite the patch—additionally failed. Each runs shipped the corruption, together with one the place the reviewer explicitly recognized the failure mode and authorised the repair anyway.
The issue wasn’t the mannequin. It wasn’t the workflow. Throughout fashions and more and more elaborate processes, the failure remained the identical.
5.2. Necessary discovering: Kind tickets by data, not issue
Cease sorting bugs by how arduous they appear. Ask a extra helpful query:
Is all the pieces wanted to provide the proper repair seen within the code and the ticket, or does correctness depend upon how the system is definitely used?

If the reply is “it’s all within the repo”—an invariant, an algorithm, a race situation—the outcomes recommend AI is a significantly better debugger than its popularity implies. I belief it way more on these bugs than I did a month in the past.
However when the repair is determined by an unspoken contract—who calls the code, what knowledge flows by means of it, what conduct callers depend on—the image modifications utterly. No mannequin or agent setup solved that drawback reliably. Worse, the unhealthy fixes nonetheless got here with inexperienced CI.
That leaves two locations with actual leverage. First, the ticket: each contract you make specific provides the agent data it can not infer from the code. One further sentence could be value greater than a mannequin improve.
Second, the evaluation gate: if an AI reviewer flags a possible aspect impact or data-corruption danger, that discovering ought to block the merge mechanically. Within the one case the place the reviewer caught the issue, the workflow ignored it. I wouldn’t design a system that is determined by somebody making the best judgment name subsequent time.
6. Limitations
The largest limitation is the arduous/straightforward labeling. I assigned these labels by instinct earlier than operating the experiment reasonably than utilizing an unbiased measure. And “discoverable from the code” could partly replicate patterns the fashions had already seen, not simply data accessible within the repository. A stronger design would have engineers, blind to the speculation, price issue beforehand.
The unstated-contract end result additionally rests on a single bug (ky PR #867). Working it throughout three mannequin tiers and three workflows makes the end result notable—12/12 failures versus 16/16 successes on the opposite bugs—however it’s nonetheless one case research, not a common regulation.
There’s additionally an asymmetry in scoring. For Immer and decimal.js, the hidden regression check already fails earlier than the repair. For ky, the reported bug is already coated; the decisive hidden check as an alternative detects payload corruption launched by the agent’s repair. Likewise, the “seen suite” for ky refers solely to the 84 retry checks protecting the affected code, not the complete suite, which has roughly 47 unrelated failures on Node 22.
Lastly, the pattern sizes are small (one to 3 seeds per configuration), so the outcomes needs to be handled as directional reasonably than definitive. “Structured investigation” refers to my very own prompting methodology, not a industrial software. Haiku was not examined on the 2 arduous bugs, leaving its functionality flooring unknown. And since all three bugs come from well-tested JavaScript/TypeScript tasks, the findings could not generalize to weaker check ecosystems. Though I chosen post-cutoff fixes to scale back contamination, the fashions should still have encountered comparable bugs throughout coaching.
7. Sources and references
Examined libraries:
- ky: HTTP shopper constructed on fetch, by Sindre Sorhus
- immer: the immutability library behind Redux Toolkit
- decimal.js: arbitrary-precision arithmetic for JavaScript
Bug reviews and fixes used as floor reality:
- ky PR #867: the straightforward bug, numeric retry restrict dropped on lengthen; its two-commit historical past incorporates each the naive repair and the correction that scoped it away from person knowledge
- immer PR #1255: the arduous bug, base state mutated after reverse/kind underneath the array-methods plugin
- decimal.js PR #260: the arduous bug, asin catastrophic cancellation close to x = 1, mounted by reformulation reasonably than precision bumping
- decimal.js challenge #249: the unique person report for the asin bug, together with the reporter’s personal precision-bump suggestion the maintainer rejected
- decimal.js PR #217: the sibling acos repair that a number of runs cited by identify — the in-repo signpost for the proper asin repair
Associated analysis:

