Apply to my task
arXiv:2610.21514 ★ 82 Oct 15, 2026

Poison Valleys: a wrong idea in the plan breaks code harder than a wrong idea in the diagnosis

Key result

The same wrong idea pushes the failure rate from 22% to 55% on one task and from 53% to 83% on another when it sits in the plan rather than the diagnosis. The authors didn't invent a new method; they measured where an error does more damage. The practical takeaway: don't hand an agent an unverified idea as a finished plan. A diagnosis leaves the model room to doubt. A plan already locks in actions: who owns the state, what order to write in, how to roll back. The model carries them into the code almost one-to-one. So pitch your hunch as a hypothesis and put a separate check between plan and code.

01

TL;DR

Poison Valleys is an experiment that shows how a mistaken idea about how code works leaks down the chain “diagnosis → plan → code”. The researchers slipped the model a mechanism that was deliberately wrong but plausible. Sometimes they presented it as the explanation of the bug (a hypothesis), sometimes as the plan for the fix. Then they ran the generated code through tests.

The headline finding: when the error sits in the plan, the model breaks the code noticeably more often than when the same error sits in the diagnosis. A diagnosis leaves the model room to maneuver: it can push back, it can pick a different implementation. A plan already locks in concrete actions: who owns the state, what order to write in, how to roll back. The model takes those and carries them straight into the code. The failure rate jumped from 22% to 55% on one task and from 53% to 83% on another.

The paper doesn't offer a method as a tool; it's more of a diagnostic. But it points to a practice for working with agents: don't hand the model an unverified plan as if it were the answer. Pitch your hunch as a hypothesis that needs checking. And put a separate check between plan and code: does the plan preserve rollback, isolation and existing contracts? The authors call this check a natural intervention point, but they didn't test it themselves.

Want this for your own case? Apply this study to your task: AI adapts its method to what you are working on and writes the prompt.

02

Method Blueprint

Here's how the chain is set up in the experiment:

STEP 1 (O): Observation — bug report and requirements → fixed
STEP 2 (D): Dependency context — neighboring code, interfaces → optional
STEP 3 (H): Hypothesis — “why the bug happens” → an error can be planted here
STEP 4 (P): Plan — “what exactly to change” → an error here hits hardest
STEP 5 (C): Candidate code → checked by the Oracle (overall test) + 18/28 targeted checks

The conditions being compared:

Error only in H   vs   error only in P   vs   error in both H and P
03

Example in Practice

The approach pays off most on tasks where a locally plausible fix violates a non-local requirement: transactions, rollbacks, shared state, caches. Those are exactly the kinds of bugs the authors built on purpose.

Task: A backend developer at a small Etsy-style marketplace is fixing batch inventory reservation in Cursor. If reservation fails on the third line item of an order, the first two stay reserved. The developer has a theory: “just wrap the loop in a single transaction.” But the helper function set_status() opens its own connection and commits on its own. The developer never checked that detail. So the theory should be pitched as an unverified hypothesis, not as a ready-made plan.

Prompt:

Task: in the function reserve_batch(items), when any item fails,
the items already reserved stay reserved.
Required: either all items get reserved, or none do.

My theory of the cause (NOT VERIFIED, treat it as a hypothesis, not a fact):
“it's enough to wrap the loop in a single transaction.”

Order of work. Don't write any code yet.

1. HYPOTHESIS. Read reserve_batch and every function it calls
   (set_status, release, publish_cache). Check my theory against the code.
   If it's wrong or incomplete, say exactly where, with line references.

2. PLAN. Describe the changes: who owns the transaction, what order the
   writes happen in, when the cache gets updated, what happens on error.

3. PLAN CHECK. Go through the plan point by point and answer “yes/no/don't know” for each, with reasoning:
   — rollback: on an error at any item, do all rows return to their original state?
   — isolation: will another connection never see a half-finished reservation?
   — identity: are the order and IDs of items preserved?
   — cache: is it updated only after commit?
   — contract: do signatures and exceptions stay unchanged?
   If even one point is “no” or “don't know,” fix the plan and check it again.

4. CODE. Only after that, write the fix.

Result: The model will first walk through the code and say whether it agrees with the developer's theory. Then it will show the plan and a point-by-point check table. If the nested function commits on its own, that should surface at step 1 or 3. Code only shows up after that. No guarantees: the authors didn't verify that this kind of check actually saves you.

04

Why It Works

The LLM weakness. A model readily follows whatever it's given. A hypothesis sounds like “maybe this is the issue” and leaves room for doubt. A plan sounds like “do it this way” and turns into operations: what to write, where to commit, how to handle the error. A wrong plan passes into the code almost directly.

The LLM strength. A model is good at checking text against a list of criteria and at matching a description to code. If you ask it at the “diagnosis” step to find the weak spot in a theory, it gets a chance to reject the error before it becomes a plan.

How the method uses this. It separates the stages and puts a checkpoint between plan and code. An error in the diagnosis hasn't locked in a solution yet. An error in the plan already has, so that's where the check belongs. One more takeaway: “failed the test” is not a single type of error. Different wrong ideas break different commitments in the code: rollback, isolation, routing, weights, compatibility. So when you ask for a repair, it pays to name what exactly broke instead of just writing “fix it.”

Prompt control levers: - How you pitch your idea (“hypothesis, unverified” ↔ “plan”) → the softer the wording, the better the odds the model rejects it. - The list of commitments in step 3 → swap in your own: idempotency, access rights, API backward compatibility, money and rounding. - The “don't write code yet” condition → without it the model jumps to code and skips the check. - Requiring line references → forces it to check the theory against the actual code instead of agreeing by ear.

05

Prompt Template

This template is derived from the paper's findings. The authors' own method is a diagnostic; the paper names plan checking as an intervention point but didn't test it.

Task: {description of the bug and the expected behavior}

My theory of the cause (NOT VERIFIED, treat it as a hypothesis, not a fact):
“{my_theory}”

Order of work. Don't write any code yet.

1. HYPOTHESIS. Study {files_or_functions} and everything they call.
   Check my theory against the code. If it's wrong or incomplete,
   explain exactly where, with line references.

2. PLAN. Describe the changes: {what_to_describe: state owner,
   order of writes, error handling, cache updates}.

3. PLAN CHECK. For each point answer “yes / no / don't know”
   with reasoning:
   {list_of_commitments}
   If there's a “no” or “don't know,” fix the plan and check it again.

4. CODE. Only after step 3, write the fix.
   Do not change {what_must_not_break}.

What to fill in: - {my_theory} — your hunch. If you don't have one, drop this block. - {list_of_commitments} — 4–6 requirements the fix is not allowed to violate. This is the most valuable part: well-chosen commitments catch “locally plausible” fixes. - {what_must_not_break} — public functions, response formats, old behavior when no new parameters are passed.

🚀 Quick start — paste into the chat:

Here's a template for checking the plan before a code fix. Adapt it to my task: {your task}.
Ask me questions to fill in the fields.

[paste the template above]

The LLM will ask what the bug is, what your theory of the cause is, and what the fix must not break. Those answers are exactly what turns into the list of commitments for checking the plan. Without them the check ends up as a box-ticking exercise.

06

Limitations

⚠️ Only two synthetic tasks: both were built by the authors on top of real Python packages (a SQLite-backed queue and shortest-path search). The direction of the effect is the same, but the authors state outright that a universal effect size is not established.

⚠️ The benefit of plan checking wasn't measured: the authors showed that an error in the plan does more damage. But whether checking the plan against a list of commitments helps, they did not test. It's a reasonable hypothesis, not a proven technique.

⚠️ The model received the error from outside: in the experiment the wrong idea was planted on purpose. In real work the model generates flawed plans on its own, and the paper doesn't study that case.

⚠️ Spread across models: the “plan vs hypothesis” gap ranged from nearly zero (one model even went slightly the other way) to +45 percentage points. The paper doesn't rank the models.

⚠️ Not about every bug: the tasks deliberately contain a non-local requirement. For simple one-line fixes the finding may not hold as strongly.

07

How They Studied It

The researchers built two tasks on top of real Python packages. In the PersistQueue queue they added batch reservation: on any error, everything must roll back. In NetworkX shortest-path search they added a filter that depends on the route's origin node. Both tasks contain a “trap”: the fix looks reasonable but violates a deep requirement.

Then they prepared “poison families” in advance. These are plausible but wrong explanations like “the status is updated in memory, so compensating is enough” (11 for the queue, 8 for routing). Each one was slipped to the model in the hypothesis, in the plan, or in both. Everything else stayed fixed. In total that came to 1844 requests to three models per task.

Results were checked at two levels. The Oracle, an overall test, says “passed or not.” Targeted checks (18 and 28 of them) show which commitment exactly was broken: rollback, isolation, identity, routing, weights, API. To keep the comparison fair, the authors balanced the set of families, models and settings across groups. After balancing, “error in the plan” produced +32.2 pp more failures on PersistQueue and +28.9 pp on Dijkstra. Adding the same error to the hypothesis as well barely changed anything: the plan was already the decisive point.

The surprise was that different wrong ideas break different commitments, but the mapping isn't one-to-one. One idea could cause several kinds of breakage, and different ideas could converge on the same one. The practical takeaway: a generic label like “the model reasoned wrong” hides what exactly broke in the code.

08

Adaptations and Extrapolations

🔧 Technique: hand the model back its failure with the violated commitments named → targeted repair.

The authors propose this as a next step but didn't test it. Verify it yourself:

Here is your previous fix and the test results.
Violated commitments:
— rollback: after an error on item 2, item 1 stays reserved;
— isolation: a second connection sees the intermediate state.
Don't rewrite everything. Find the decision in your plan
that led to these violations, fix the plan, then the code.

The idea is to go back to the plan stage instead of patching the code.

Extrapolation (mine, not tested in the paper): you can add a rule to the agent's instructions file (CLAUDE.md, AGENTS.md).

“Before changing code that touches transactions, caching or a public API, write out a plan and check it against the list: rollback, isolation, ordering, compatibility. Treat my theories of the cause as hypotheses and verify them against the code.”

09

Resources

  • Poison Valleys: Diagnosing Path-Dependent Failure in LLM Code Repair — Veljko Skarich
  • Code, data and demo: poisonvalleys.org
  • Related work cited in the paper: Obey, Diverge, Collapse (Jaiswal et al., 2026), STAR (Wang et al., 2026), DoVer, REFLECT, CausalFlow
  • Base projects: persist-queue (Wang, 2025), NetworkX 3.6 (Hagberg et al., 2008)

Research digest

Key result

The same wrong idea pushes the failure rate from 22% to 55% on one task and from 53% to 83% on another when it sits in the plan rather than the diagnosis. The authors didn't invent a new method; they measured where an error does more damage. The practical takeaway: don't hand an agent an unverified idea as a finished plan. A diagnosis leaves the model room to doubt. A plan already locks in actions: who owns the state, what order to write in, how to roll back. The model carries them into the code almost one-to-one. So pitch your hunch as a hypothesis and put a separate check between plan and code.

Principle

The chain works like a relay race: diagnosis, then plan, then code. An error in the diagnosis may still never reach the finish line. An error in the plan has already turned into commands, and the model just executes them. It's the difference between “maybe the faucet is leaking” and “shut off the water and take the faucet apart.” People carry out the second phrase; they mull over the first. Pitch your idea as “hypothesis, unverified,” not “do it this way”. Between plan and code, put a checkpoint with a list of commitments: rollback, isolation, existing contracts.

Why it works

A model readily follows whatever it's given. A hypothesis sounds like “maybe this is the issue,” which leaves room for doubt. A plan sounds like “do it this way.” Operations grow straight out of it: what to write, where to commit changes, how to catch the error. Wild: swapping the word “hypothesis” for “plan” raised the failure rate from 53% to 83%. The model's strength lies elsewhere. It's good at checking text against a list of criteria and a description against code. If at the diagnosis step you ask it to find the weak spot in your theory, it may reject the error before it turns into a plan. One more takeaway: “the test failed” doesn't always mean the same error. Different wrong ideas break different things: rollback, isolation, routing, weights, compatibility. So when you ask for a repair, name what exactly broke instead of writing “fix it.” Honest caveat: the authors didn't measure the benefit of plan checking. They only measured the harm of an error in the plan. Plan checking here is a reasonable hypothesis, not a proven technique.

When to apply

Development with agents (Cursor, Claude Code and the like) → fixes where a locally plausible solution breaks a non-local requirement: transactions, rollbacks, shared state, caches, access rights, money and rounding → especially when you already have your own theory of the cause but haven't read all the code being called. NOT a fit for simple one-line fixes: there the finding may not hold as strongly. The data is narrow: two made-up Python tasks (a SQLite-backed queue and shortest-path search), with the spread across models ranging from nearly zero to +45 percentage points. In the experiment the error was planted on purpose rather than generated by the model itself.

Mini recipe

1. Call a hunch a hunch: write “my theory of the cause (NOT VERIFIED, treat it as a hypothesis, not a fact).” 2. Ban code at the start: add “don't write any code yet.” Without it the model jumps straight to the fix. 3. Make it check against the code: have it study the function and everything it calls. If the theory is wrong, have it name the lines. 4. Ask for the plan separately: who owns the transaction, what order the writes happen in, when the cache updates, what happens on error. 5. Pick 4-6 commitments: rollback, isolation, order and IDs, cache after commit, signatures and exceptions. Swap in your own; this is the most valuable part. 6. Have it check the plan: “yes / no / don't know” for each point, with reasoning. Any “no” or “don't know” means: fix the plan and check again. 7. Only then code: and add a list of what must not change: public functions, response formats, old behavior.

Examples

[BAD]: `Inventory reservation fails on the third item, and the first two stay reserved. Wrap the loop in one transaction and fix the code` [GOOD]: `Task: in reserve_batch(items), when any item fails, the earlier items stay reserved. Required: all or nothing. My theory of the cause (NOT VERIFIED, treat it as a hypothesis): “it's enough to wrap the loop in a single transaction.” Don't write any code yet. 1. Read reserve_batch and everything it calls (set_status, release, publish_cache). Check my theory, point to the lines. 2. Describe the plan: who owns the transaction, order of writes, when the cache updates. 3. Check the plan point by point with “yes/no/don't know”: rollback, isolation, order and IDs of items, cache only after commit, signatures unchanged. On any “no,” fix the plan. 4. Only then write the fix.` What this gets you: set_status() opens its own connection internally and commits changes on its own. Simply wrapping everything in one transaction won't save you. That detail should surface at step 1 or 3, before any code appears. No guarantees: the authors didn't verify that this kind of check saves you.

In plain words

An LLM can't second-guess itself in hindsight: feed it a crooked idea at the start and it will dutifully march that idea all the way to disaster. The phenomenon got a name, Poison Valleys: a pathological dependence on context. The model reasons strictly down a chain, from hypothesis to plan to code. Put a drop of poison at the top of that chain by slipping in some plausible nonsense, and the model will happily drag the error through every stage and hand you broken code, sincerely convinced it fixed the system.

It's like calling in a rookie plumber and announcing confidently at the door: “I think a pipe burst in the kitchen.” Instead of checking the main riser, he starts smashing the tile behind the stove, simply because that authoritative hint is already stuck in his head. Locally his actions make sense, since he's fixing the spot he was pointed at, but at the scale of the building he ends up flooding the downstairs neighbors because of one wrong nudge.

Paper 2610.21514 lays out the mechanics clearly: the most dangerous thing isn't an abstract hypothesis but a directive fix plan. With wording like “maybe this is the issue” the model can still push back, but a command like “do step one, step two” switches its critical thinking off entirely. The pattern reliably tanks code on tasks with non-local requirements: transactions, caches and rollbacks are the first to break when the model blindly slaps on a pretty local band-aid.

The study looked at automated code fixing, but the principle is universal for any complex system. Any autonomous AI agent working through a chain of thought is vulnerable to this bug: in data analytics, auditing or legal work. A false premise in step one doesn't fade out further down; it compounds exponentially, turning the final output into overconfident, well-argued total garbage.

Bottom line: never feed the model a ready-made solution plan unless you're 100% sure of it yourself. Make the model hunt for the root cause from scratch, or it will just run off and execute your crooked instructions. Control the input hypotheses instead of scrubbing bugs at the end: cleaning up after someone else's confidence always costs you more.

AI lab assistant

Apply this study to your task

Describe what you're working on. AI takes the method and the measured results from this paper and adapts them to your case, or writes a ready prompt built on them.

Based on Poison Valleys: a wrong idea in the plan breaks code harder than a wrong idea in the diagnosis arXiv:2610.21514

Sign in to apply this study New accounts get 50 free credits.