August 2026
A systematic investigation into contextual drag in Qwen3-8B.
This post uses a controlled environment (Game-of-24) on Qwen3-8B to understand contextual drag (failed attempts bias a model to repeat the same errors). I trace it to two separable failures (a rubber-stamp self-check, and a weak constrained search) and show that teaching the model to fix the rubber-stamp self-check does not restore accuracy, because search is the limiting factor.
A lot of what we want reasoning models to do can be understood as search:
A promising attempt should narrow the search; a failed one should guide it somewhere else.
If you squint enough, this search loop appears across many domains:
Again, we're squinting from a distance. What we're searching differs (plans, molecules, treatments, solutions, proofs) and the environmental feedback differs (terminals give you error messages; simulators give you scores; environments give you rewards). But the hope is the same: the system can use the outcome of one attempt to make the next better.
Contextual drag threatens that assumption. Cheng, Zhu, Zhao, and Arora define contextual drag as the tendency of failed attempts in the context to bias later generations toward structurally similar errors. A failure that should repel the search ("don’t go here") instead exerts a kind of pull ("try more of this").
Across 11 models and eight reasoning tasks, the authors report performance drops of roughly 10–20%. In models especially susceptible to contextual drag, iterative self-refinement can even become self-deterioration. More strikingly, explicit feedback does not reliably remove the bias, and a model may remain anchored to an erroneous approach even after correctly identifying it as wrong.
At the model level, these loops require two distinct capabilities. The model must search: propose a candidate that satisfies the task's constraints. And it must evaluate and steer: determine why a candidate succeeded or failed, then use that information to guide the next proposal.
This post uses a controlled testbed on Qwen3-8B to pull those capabilities apart, and finds that both can break. At first, evaluation is little more than a rubber stamp: the model declares that an answer satisfies the constraints even when a simple count shows otherwise. But teaching it to perform that check correctly still does not improve accuracy. The visible error disappears, only to be replaced by a different one, revealing the second failure: the model cannot reliably search for solutions while respecting all the constraints at once.
Contextual drag is therefore not just a failure to recognize errors; it also exposes a weakness in the search needed to escape them.
I run everything on Qwen3-8B, one of the models the paper studies, and before anything else I confirmed the drag effect reproduces in my setup. The task is Game of 24: combine four numbers with + - * / and parentheses, each number used exactly once, to make 24. It has a perfect verifier (evaluate the expression, check the arithmetic and the number usage), so there is zero label noise.
I compare two prompts on the same puzzles.
Nothing else differs, so any accuracy gap between them is contextual drag.

The result: a 47.9pp drop in performance! Which is consistent with the paper's finding of a 53pp drop (from 76 to 23). Their numbers are slightly different only because they report a subset of the harder problems (653/1362).
| Condition | Accuracy | Note |
|---|---|---|
| Base, no-drag (DIRECT) | 85.9% | what the model can do without drag |
| Base, 2F drag | 38.0% | the drag drop: -47.9 pp |
A model that solves these puzzles 86% of the time collapses to 38% when two wrong examples are in front of it. For comparison, a stronger model (GPT-OSS-20B) on the identical setup drops only 15.4 points (88.8% to 73.4%), which matches the paper's finding that weaker models are more susceptible. So the phenomenon reproduces, it is severe on Qwen3-8B, and Game of 24 gives us a clear test environment to study it.
Contextual drag looks like a robustness problem. Two incorrect drafts are noise that disturbs the model. Supervised fine-tuning (SFT) is the obvious fix: train the model to produce the correct answer even when the drag prompt is present.
The recipe (train the drag prompt toward the clean answer). For each puzzle I took one target: the model's own correct solution, sampled from the DIRECT prompt where no drafts are shown. The training set is a 50/50 mix of two input types, both mapped to that same draft-free target:

The second prompt is the key mechanism: feed the drag prompt, but train toward the answer the model gives when the drafts aren't there. I trained a LoRA adapter on Tinker on this mix.
Results. The accuracy under drag jumped from 38.0% back to 87.1%. But two hypotheses explain that number:
We want a model that uses context conditionally (H1), rejecting wrong drafts and adopting right ones, because a failed attempt should push the search away and a correct one should pull it in.
But the recipe suggests H2. Its most likely signal is "the drafts don't change the answer," so the cheapest thing for the model to learn is to stop reading them.
Separating H1 from H2. Our current metric, 2F accuracy (F = two false drafts), can't separate the hypotheses: it only tests whether bad context hurts. So I added a control, 2T (T = two true drafts): the same prompt, but the two drafts are now correct solutions the model should use.
The 2T test only means something on puzzles the model can't already solve on its own. If I hand it correct drafts for a puzzle it solves anyway, a right answer proves nothing. So 2T runs on a hard subset: 14 puzzles the base model solves only 29.5% of the time from scratch (1 to 3 times out of 8). On these, the only way to score above 29.5% is to actually read and use a correct draft. H1 predicts 2T shoots up toward 100%; H2 predicts it stays stuck near the 29.5% no-draft rate.
The verdict is H2. The base model scores 99.1% on 2T: it reads the correct drafts and rides them from 29.5% to near-perfect, which is exactly why wrong drafts can drag it the other way. The fine-tuned model scores 87.1% on 2F but 29.5% on 2T, exactly its no-draft rate, meaning it ignores the correct drafts entirely. SFT "fixed" contextual drag by going context-blind.
2F and 2T are measured on different puzzle sets (2T on the 14-puzzle hard subset), so read each column on its own rather than comparing 2F to 2T within a row.
| Model | Wrong-draft eval (2F) | Correct-draft eval (2T, hard subset) | Note |
|---|---|---|---|
| Base Qwen3-8B | 38.0% | 99.1% | dragged by wrong drafts, but rides correct ones |
| SFT: map drag prompt → correct answer | 87.1% | 29.5% | immune to wrong drafts, but now ignores correct ones |
| Baseline: no drafts at all | 85.9% (full set) | 29.5% (hard subset) | reference floors/ceiling, not a draft eval |
So "beating contextual drag" cannot just mean "same answer regardless of context," or the trivial winner is a model that ignores context. The capability worth building is selective context use, and it is harder.
Before trying to improve on the 38%, it helps to see exactly how the base model fails when it is dragged.
I looked at the failures behind that 38% and categorized them. Game-of-24 is convenient here because a verifier gives the true answer for free. The verifier parses each boxed expression and checks two things: does it use exactly the puzzle's numbers, and does it evaluate to 24? Those checks sort each failure into the following mutually exclusive buckets:
wrong_numbers,no_boxed answer,wrong_value, orunparseable into arithmetic.Most of the failures are wrong_numbers, so I split the largest bucket into sub-types after further inspection. Here is the base model's failure distribution under the 2F drag prompt, as a share of its wrong answers:
| # | Failure bucket | Description | Share of failures |
|---|---|---|---|
| 1 | wrong_numbers | uses the wrong multiset of numbers | 90.5% |
| 1a | · reuse | duplicates a puzzle number to force 24. On [1, 4, 6, 7]: 6 × (4 + 1) - (7 - 1) = 24, using the single 1 twice | 45.5% |
| 1b | · boxed_24 | gives up on an expression and boxes the answer value itself. On [1, 2, 8, 9]: returns \boxed{24} instead of an expression built from the numbers | 43.7% |
| 1c | · missing_extra | drops a puzzle number, or adds one that was never there | 1.3% |
| 2 | no_boxed | never reaches a boxed final answer within the token budget | 8.9% |
| 3 | wrong_value | uses the right numbers, but the arithmetic does not equal 24 | 0.5% |
| 4 | unparseable | not valid arithmetic | 0.1% |
It breaks the counting constraint while claiming it didn't. Failures are almost all wrong_numbers (90.5%) and almost never wrong_value (0.5%). The model can reach 24; what it cannot reliably do is reach 24 while using each number exactly once. The two dominant flavors above tell the story: it duplicates a number (45.5%), or gives up and boxes the target value itself (43.7%). Usually, its own reasoning still declares "each number is used exactly once." The self-check is present in the words and absent in fact.
Each of these failure modes could suggest a different intervention. I could imagine constructing a new data mix and using SFT to fix a number of them. But I first wanted a different cut of the same failures: how do the wrong answers relate to the two failed drafts sitting in the prompt?
Wrong answers often copy the failed drafts. This one cuts across all the buckets above: a copied answer is still counted as wrong_numbers or wrong_value. Measured separately, 41.7% of the model's wrong answers reproduce, verbatim, a boxed expression from one of the two wrong drafts it was asked to evaluate (excluding the trivial 24). The failures are not random. Nearly half are inherited straight from the context. That is a very strong demonstration of how contextual drag works: asked to judge two wrong attempts, the model ends up copying them.
These two failures are why the search process breaks down. The model trusts bad context instead of steering away from it, and its self-verification is a rubber stamp that yields no honest failure signal. The natural tool for both problems is the verifier. So the question becomes: given a verifier, what do you do with it?
The verifier is the exact tool that ought to fix the broken search loop: it tells you, without noise, whether an attempt succeeded. So: given a perfect verifier, what is the best way to spend it to build drag resistance? I tried the natural things, in roughly increasing order of how much they let the model learn from its own behavior.
Supervised fine-tuning on verified winners. I first tried rejection fine-tuning (RFT): the basic idea is to just take more samples from the model (with the 2F drag prompt) until the model gets it right. Then, fine-tune on those completions. On its own, this lifts accuracy under drag from 38.0% to 56.6%, and it doesn't degrade 2T at all.
| Method | 2F accuracy (drag) | 2T accuracy (correct drafts) |
|---|---|---|
| Base (no fine-tuning) | 38.0% | 99.1% |
| RFT (no hint) | 56.6% | 100% |
Adding hints. How can we do better? I wanted to a try an idea inspired by Cursor's training recipe for Composer 2.5. Verifiers provide true feedback, but it's only binary. It tells you that an attempt failed, but not where or why. Further, we currently end up discarding a lot of rollouts because the model fails most of the time. This is a lot of wasted information.
The idea is pretty simple: give the model hints from the verifier. A hint is injected right before the model makes a wrong decision to prevent it from going down a search path we know will be wrong.
Cursor used it as part of a more complex setup, but I started with a simpler version of the same idea: use the hint only to recover more winners, and then use those winners for RFT/SFT. When a rollout failed, a hint ("use each number exactly once", or a diagnosis-driven or LLM-written variant) was added, the model retried, and I trained on the recovered successes.

I used five hint variants, from a deterministic oracle to an LLM writer that saw the failed attempt.
The teacher is the model (same Qwen3-8B model) given the hint, retrying a failed rollout to recover a correct answer. The student is the model I then fine-tune on those recovered answers and evaluate, always without the hint. This is technically off-policy, but the hope is that it's not too far off-policy. The two columns show each metric: recovery is how often the teacher fixes a failure, and 2F accuracy is the student's held-out accuracy under drag. A better teacher does not have to mean a better student.
| Hint arm | Example hint | Recovery (teacher) | 2F accuracy (student) |
|---|---|---|---|
| 1. No hint (plain RFT, baseline) | (none; the teacher just resamples) | 46.2% | 56.6% |
| 2. Generic nudge | "Be careful; reconsider the problem and try a different solution." | 49.0% | 51.0% |
| 3. Oracle (rule-based diagnosis) | "Use all four numbers exactly once... include 1, 1, 1, 13. Do not introduce numbers that aren't in the puzzle (avoid 24)." | 52.4% | 53.8% |
| 4. LLM writer (saw drafts) | "...the last attempt used 24 instead of the given numbers. Ensure you use only 1, 1, 1, and 13." | 49.2% | 55.5% |
| 5. LLM writer (no drafts) | "Use the correct numbers from the puzzle... 1, 1, 1, 13... each used exactly once." | 50.8% | 53.6% |
Recovery is measured on the same 1,495 failed student rollouts for every arm (4 teacher retries each). Arm 1 is the baseline: the teacher just resamples with no added signal and recovers 46.2%, so each hint arm's lift over 46.2% is what the hint improves. Example hints all target the same failed rollout: puzzle [1, 1, 1, 13], where the model had lazily boxed 24 (bucket boxed_24 from Section 3). The generic nudge is fixed; the oracle reads the verifier's diagnosis; the LLM writers read the failed rollout.
The result is counterintuitive. Adding hints raised the teacher's recovery but never raised the student's accuracy. The no-hint arm has the lowest recovery (46.2%) yet the highest final accuracy (56.6%); the oracle hint recovers the most failures (52.4%) but trains a middling student. A better teacher signal bought a worse student, or at best no gain.
The reason is classic off-policy training. The hint is in the teacher's prompt, but it is stripped before training the student, because at evaluation time there is no hint. So the student is trained to produce hint-influenced reasoning from a prompt that never contained the hint, and it cannot reconstruct the missing influence. Hint-conditioned SFT has a ceiling set by how much of the hint's effect survives into a target the student can reproduce unaided. Recovery rate measures the teacher's capability, not what the student can learn. Just chasing a teacher's recover rate won't improve the student.
On-policy RL. The methods so far learned from a teacher's rollouts. GRPO (group-relative policy optimization) lets the model learn from its own. Starting from the base model, I sample 8 rollouts per puzzle on the 2F drag prompt and grade each with the verifier: a correct rollout is rewarded, a wrong one penalized, with each rollout scored relative to the average of its own group of 8. Rollouts above their group's average get reinforced, and those below it get pushed down. I ran three rounds of this on 300 training puzzles.
GRPO clears SFT and reaches 64.4%, up from 38% at base and 57% for RFT. It is the one real jump in the study, and the first round does almost all of it (+23 pp). Like RFT, it stays in the verify-then-act regime (2T is still 100%), so it keeps reading the context rather than learning to ignore it.
What it does not fix is draft-copying: when GRPO is wrong, its answer still resembles one of the drafts about as often as the base model's does (roughly 25% of wrong answers). A binary reward rewards a correct answer however it was reached, so nothing pushes the model to stop copying.
GRPO also has a built-in blind spot. It only learns from puzzles where the 8 rollouts are a mix of right and wrong, because only those produce a gradient. A puzzle the model never solves in 8 tries gives an all-wrong group, no gradient, and no signal. Call these the stuck puzzles. As training saturates the easy puzzles (solved 8/8), the pool that still produces a gradient shrinks (196 puzzles, then 90, then 60 over the three rounds), and the stuck puzzles never enter it.
So I tried four ways to push past 64% by managing the successes and failures within a rollout.
Drag-penalized reward. To attack the copying directly, I changed the reward to correct − λ · (looks like a draft), penalizing wrong answers whose expression is structurally close to one of the drafts. It did nothing: accuracy stayed flat (slightly down), and copying did not fall, at λ=0.5 or at 4× that. The drafts are near-misses, so correct answers often resemble them too. Penalizing "looks like a draft" also penalizes correct answers, so I think that's why this didn't work.
Warm-starting from the hint-trained model. Starting RL from the best SFT adapter instead of from base reached the same ~65% ceiling, just faster. A better starting point solves the easy puzzles sooner, but it does not pull the stuck puzzles into the solvable range, so they still give no gradient and the ceiling is unchanged.
On-policy self-distillation. Instead of a binary reward, give the model a dense per-token target from a teacher along its own rollouts. Here the teacher is the base model itself, shown a correct solution to the puzzle in its context, so this is self-distillation: the same weights teaching themselves, differing only by having the answer in view. This is the setup of Self-Distilled Reasoner (Zhao et al.: on-policy self-distillation with a solution-conditioned teacher) and the closely related SDFT (Shenfeld et al.). It fell below base, to 33.4%. The teacher is not actually stronger; it only has a hint. The dense signal largely agreed with the student's existing behavior and reinforced it.
Injecting hint-found solutions. When a hint let the model solve a stuck puzzle, I fed that solution back into the RL update with the hint removed. The found solutions did not transfer: they did not raise the held-out ceiling, and did not durably stick even on the training puzzles they targeted. A much stronger dose bought only about a 9% per-attempt chance on held-out stuck puzzles, not reliable solving.
The tempting conclusion is a coverage ceiling: the stuck puzzles are simply too hard for the model to ever sample correctly, so no method that learns from sampled successes can touch them. This fits the training curves cleanly. It is also wrong, and finding out why is the point of the next section.
| Condition | 2F acc (drag) | 2T acc (correct drafts) | Note |
|---|---|---|---|
| Base, no-drag prompt (reference) | 85.9% | — | what the model can do without drag |
| Base | 38.0% | — | the drag drop: -47.9 pp |
| Ignore-context SFT | 87.1% | 29.5% | best 2F, but ignores good context |
| HC-RFT (SFT on verified winners, best of 5 hint arms) | 56.6% | 100% | lower 2F, but uses the context |
| GRPO (on-policy RL) | 64.4% | 100% | the one real jump; still uses the context |
| ↳ Drag-penalized reward | 63.0% | 96.4% | penalize draft-like wrong answers; no gain |
| ↳ Warm-start from SFT | 63.1% | 98.2% | same ceiling, reached faster |
| ↳ On-policy self-distillation | 33.4% | 100% | below base; teacher endorses the copy |
| ↳ Hint-injection on RL | 63.9% | 98.2% | found solutions don't transfer |
After trying several methods, it paid off to pause and read the outputs on the stuck puzzles.
On [1, 2, 7, 12] the model wrote 12 × (7 - 1) ÷ (2 + 1), which equals 12 × 6 ÷ 3 = 24. The value is right, so at a glance the answer looks correct. But count the numbers: 12, 7, 1, 2, 1. It used the number 1 twice, once in 7 - 1 and once in 2 + 1. The puzzle has a single 1. The expression is invalid. And in its own reasoning, the model certified it and wrote "all numbers are used exactly once". This is the "rubber stamp" self-verification.
On 20 held-out stuck puzzles, 84% of attempts fail with wrong-numbers, and 99% of those specifically reuse an in-puzzle number, almost always the 1, by building two sub-terms that each consume it. The model reaches the right value and miscounts its own number usage while explicitly claiming it counted. (This is the same number-reuse the base model showed in Section 3. There it was an aggregate rate in a table; here, reading the actual outputs revealed its specific error.)
Then I ran the exact solver on the stuck puzzles. Every one is solvable. [1, 3, 8, 13] has more than 100 valid solutions; [1, 2, 7, 12] is solved by 2 × (7 - 1) + 12, no division needed (which I initially thought might be more difficult for the model). If the theory was some puzzles are harder or have fewer valid solutions, that wasn't right. The model was failing solvable puzzles by reusing a number and rubber-stamping its own count.
The obvious fix is to teach the model to be better at self-verification. It should catch its own number-reuse in these cases and correct its error.
Training data. Ordinary fine-tuning on correct answers cannot teach this: a correct trajectory and a reusing one both end with "all numbers used exactly once, correct," and the only difference is the final expression, so the data never shows the act of noticing a reuse and fixing it. So I built data that does. I took the model's own reusing rollouts and, using the verifier's ground truth for which number was reused, appended an explicit count that catches it ("I used the 1 twice; the puzzle has one 1; invalid") followed by a verified correction. The prompt stays plain, so there is no train/eval mismatch. I trained this on top of the RL policy.
The self-check trained cleanly. On held-out stuck puzzles, number-reuse dropped from 84% to 16%: the model now enumerates the numbers it used, catches a reuse when there is one, and counts correctly. The visible failure was fixed.
But accuracy did not improve. It fell slightly, from 63% to 60%. When the model stops reusing numbers, its failures do not turn into successes; they turn into a different failure. The dominant error shifts from wrong-numbers to wrong-value: the expressions now use the exact number set but do not equal 24. When the model is forced to respect the constraint, it cannot find a solution inside it.
The 2T control shows the same thing from the other side. Base and RL scored ~100% on 2T not by verifying the correct drafts but by copying them wholesale. Catch-and-correct trained the model to check its own numbers instead of rubber-stamping, so its draft-copying on 2T dropped from 93% to 57%. Once it stops copying, it has to solve the puzzle itself, and its 2T lands at 64%, its own solve rate. Fixing self-verification removed the copying that had been hiding the same weak search.
What does this tell us? Number-reuse was a symptom, not the cause. The model reuses a number because it cannot find an expression that makes 24 and uses each number once, so it drops the constraint it is worst at tracking. Teaching it to stop does not supply the missing search ability. The real bottleneck is constrained search, and none of the tools I tried (extra signal, distillation, hints, self-verification) fixes it.
The finished scoreboard:
| Condition | 2F acc (drag) | 2T acc (correct drafts) | Note |
|---|---|---|---|
| Base, no-drag prompt (reference) | 85.9% | — | what the model can do without drag |
| Base | 38.0% | — | the drag drop: -47.9 pp |
| Ignore-context SFT | 87.1% | 29.5% | best 2F, but ignores good context |
| HC-RFT (SFT on verified winners, best of 5 hint arms) | 56.6% | 100% | lower 2F, but uses the context |
| GRPO (on-policy RL) | 64.4% | 100% | the one real jump; still uses the context |
| ↳ Drag-penalized reward | 63.0% | 96.4% | penalize draft-like wrong answers; no gain |
| ↳ Warm-start from SFT | 63.1% | 98.2% | same ceiling, reached faster |
| ↳ On-policy self-distillation | 33.4% | 100% | below base; teacher endorses the copy |
| ↳ Hint-injection on RL | 63.9% | 98.2% | found solutions don't transfer |
| Catch-and-correct (self-verification) | 60.4% | 64.3% | real verification stops the rubber-stamp copying (2T copy 93%→57%), so on 2T it must solve itself and lands at its own ~64% rate |
We started with this search loop:
We found where the model was failing, the verify step: its self-verification is a rubber-stamp, writing "each used once" while miscounting, so it gives no usable signal about its own attempts. But when we fixed that with catch-and-correct, accuracy still did not move. The propose step is the remaining bottleneck: the model cannot reliably search for a valid solution when it sees 2 failures in context.
That leaves the question I want to take up next: why is the search step so weak specifically under drag? Remember, the model can solve these problems, just as long as it doesn't see a wrong answer in the prompt. The 2F prompt makes the model do several things at once within a fixed capacity. It has to hold two wrong drafts in context, resist copying them, check them, and still search for an expression that reaches 24 using each number exactly once. Constrained search is the most fragile of these. My working hypothesis is that the drafts compete for the capacity the search needs, so under that load the model tracks the number budget less carefully and falls back on the cheap move: reuse a number, or copy a draft. If that is right, contextual drag is not only an output bias where wrong examples pull the answer toward them. It is partly an interference effect, where the extra context consumes capacity the task itself requires.
Behavior cannot settle that; the test is to look inside. Anthropic's global workspace work gives a handle. Their Jacobian lens finds a J-space: a small, limited-capacity set of internal representations, a few dozen concepts at any moment, that carries the reasoning a model can actually report and control. That fixed capacity is exactly the bottleneck the hypothesis predicts. The next post reads the J-space while the model works the DIRECT and 2F versions of the same puzzle, testing whether the drafts crowd out the number-tracking and search that the clean prompt keeps intact: a kind of J-space exhaustion.
Pulling out the key learnings from evaluation:
This is one 8-billion-parameter model on one toy task with an enumerable failure space. I am not claiming the specific number-reuse bug is universal, and the deepest finding (constrained search is the residual wall) is entangled with Game of 24's structure.
Next, I'd like to investigate some mechanistic interpretability questions. The model claims "each number used once" but it is wrong. Is that because the model's counter is genuinely wrong, or because the verification separate from any internal count at all? I don't think behavior alone can separate these. A follow-up will read the model's internal "workspace" directly, using the Jacobian-lens method, while it solves and fails these puzzles.