Cursor Agent drops repeated characters in words (e.g. 9944→994/944, tiann→tian, oos→os, *.mmd->*.md)

,

Where does the bug appear (feature/product)?

Cursor CLI

Describe the Bug

Cursor Agent often drops one character from known repeated-letter/digit sequences when freely recalling or writing mid-prose identifiers - even in short “reply with only X” answers, and sometimes after reading a document that contains the correct spelling.

Observed corruptions (correct → emitted):

This is not a display/render bug in a wrapper: the wrong string is present in the agent message payload / ACP transcript. Short cursor-agent --mode ask one-liners often succeed; Agent mode (tools + multi-turn) reproduces reliably.

Affects multiple Cursor-selectable models in the same Agent path (Auto, Composer 2.5, Claude Opus via Cursor, GPT/Codex via Cursor) — not limited to one SKU.

Steps to Reproduce

  1. On Linux, Cursor CLI 2026.07.17-3e2a980 (Ultra).

  2. Start Cursor Agent (ACP) in a repo that knows an estate hostname with doubled digits/letters guest oos-linux, upstream GitHub - tiann/hapi: App for Claude Code / Codex / Gemini / OpenCode, vibe coding anytime, anywhere · GitHub). Do NOT put the correct spelling in the user prompt and do NOT say “be careful about doubles.”

  3. Use a casual prompt, e.g.:

  • “Where do I open the HAPI web UI over the tailnet for this estate? Reply with only the https origin (no path).”
  • “Which host runs the HAPI hub on this estate? Hostname only.”
  • “Give the full GitHub URL for issue 878 in the upstream hapi repo under the tiann org.”
  • Or a short prose ask that naturally includes those links.
  1. Run the same prompts under several --model values: auto, composer-2.5, claude-opus-4-8-medium, gpt-5.3-codex-low.

  2. Inspect the agent’s own message text (not UI chrome).

Repro matrix we ran 2026-07-20 (Agent/ACP sessions):

Expected Behavior

When the model intends a known identifier with repeated characters, the emitted text should preserve those characters exactly (tail112233ee, tiann, oos-linux). Reading a doc with the correct spelling and then rewriting the same hostname/URL should not drop a digit or letter.

Operating System

Linux

Version Information

CLI:
CLI Version 2026.07.17-3e2a980
OS linux (x64)
Subscription Tier Ultra

(IDE: fill from Menu → About Cursor → Copy if filing as IDE as well)

For AI issues: which model did you use?

Reproduced across Cursor Agent model choices in one matrix:

  • auto
  • composer-2.5
  • composer-2.5-fast
  • claude-opus-4-8-medium
  • gpt-5.3-codex-low

Additional Information

Proof excerpts from Cursor Agent message payloads (2026-07-20). Wrong forms are in the agent message text itself.

  1. Model: claude-opus-4-8-medium (Cursor Agent)
    Cursor chat id: 2740e5b2-1c6f-4aff-a6e9-fc4f0f775e45
    Emitted: https://site.tail12233ee.ts.net
    (correct estate hostname is site.tail112233ee.ts.net — dropped one “1”)

  2. Model: gpt-5.3-codex-low (Cursor Agent)
    Cursor chat id: adbb987-f987-47c1-ad2b-d6020510096a
    Emitted hostname: os-linux (correct: oos-linux)
    Emitted URL: https://site.tail11223ee.ts.net (correct: …tail112233ee…)

  3. Model: composer-2.5 (Cursor Agent)
    Cursor chat id: 965af036-9901-408d-9a71-8f9e6350cb0f
    Same session emitted correct https://site.tail112233ee.ts.net AND later:
    https://site.tail12233ee.ts.net
    https://github.com/tian/hapi/issues/878 (dropped “n” from tiann)

  4. Model: auto (Cursor Agent)
    Same pattern: correct site.tail112233ee.ts.net plus later site.tail11223ee.ts.net / github.com/tian/…

Control notes:

  • This is NOT a third-party UI render bug: strings appear in agent/ACP message content.
  • cursor-agent --mode ask with an explicit “output exactly site.tail112233ee.ts.net” style prompt often PASSES — the failure is free recall / agent prose, not “cannot emit the characters ever.”
  • Standalone non-Cursor agent runners for Claude/Codex are outside this report; here all failures are under Cursor Agent with different --model values.

Happy to attach full transcripts or re-run with privacy disabled for Request IDs.

Of note - even in the construction of this bug report, there were MANY missing characters, from words that had doubled characters (like “missing” - this is a PERVASIVE issue)

Does this stop you from using Cursor

Sometimes - I can sometimes use Cursor

Hey @gavinc,

This isn’t a bug in Cursor’s text pipeline - Cursor forwards the model’s output verbatim; nothing in the agent/CLI path edits or de-dupes characters. The dropped characters come from the model itself during free recall. That’s why it’s non-deterministic, why the same session emits the string correctly and then wrong, why “output exactly X” passes, and why it happens across Auto/Composer/Opus/GPT alike (a Cursor-side bug wouldn’t vary by model). It’s the same tokenization weakness behind the classic “how many r’s in strawberry” problem.

It’s a known LLM limitation rather than something with a Cursor-side fix, but a few things reliably help:

  1. Keep the exact identifiers in a project rule or AGENTS.md so they sit right where the model generates. Rules
  2. Have the agent copy the string from source with a tool (grep/cat) and verify URLs/hostnames against the file instead of recalling them.
  3. Higher reasoning-effort model variants tend to be less prone than the low/medium ones you tested.

Let me know if copy-and-verify helps.

Sanitized. The tailnet hostname used in the proof set (hapi.tail7733ee.ts.net) is a fabricated stand-in with the same doubled-character shape as our real one; tiann and oos-linux are real public strings.


Hey Mohit - appreciate the detailed reply. I conced you’re right on several points, I’ll try and be fair about them before I push back:

  • Non-determinism (same session correct-then-wrong) and “output exactly X” passing are both consistent with sampling/recall. Yep, agreed.
  • I’m not claiming Cursor’s code edits or de-dupes bytes. I accept the model generates the wrong string; nothing in the CLI/agent path is rewriting text.

But the core of your argument — “it happens across Auto/Composer/Opus/GPT alike, and a Cursor-side bug wouldn’t vary by model, therefore it’s the model, not Cursor” - that doesn’t really hold, in my day to day experience, so I “know” it’s not right, so I went away to make a control experiment to show why.

The logic gap

“Varies across models ⇒ not Cursor” only rules out a bug in a model-specific code path (e.g. a Composer-only bug). I believe it does not rule out a factor in the shared layer that sits in front of every model you route - context assembly, the agent/system scaffolding, and your default sampling/effort settings. Those apply uniformly to Auto/Composer/Opus/GPT, so they’d produce exactly the “model-agnostic” pattern you’re pointing at. Basically, cross-model variance is evidence against a per-model bug - not evidence against a Cursor-layer cause.

The only test that separates “the model does this regardless” from “Cursor’s shared layer induces it” is running the same models outside Cursor. That was the missing control, so I went ahead and ran the thing.

The data (same models, driven directly - without Cursor)

Claude Opus 4.8 (Anthropic’s CLI) and OpenAI’s Codex (codex-cli 0.145.0), driven directly with no Cursor router/Composer/ACP, on the same class of sly prompts:

Regime Trials Doubled-char drops
Free recall of a known doubled-letter string (tiann) 11 0
Copy a doubled-character host into prose 8 0
Distance-recall (string given earlier in session → distance → regenerate in prose from memory) 100 emissions 0

Over 125 emissions of tiann / oos-linux / a doubled-digit hostname, zero drops. The distance-recall arm is the important one: it reproduces the exact situation the agent is in - the identifier lives earlier in the session (rules/history) and the model regenerates it later in prose under load. Verbatim, unedited receipts (fabricated host so I can share):

Claude: https://hapi.tail7733ee.ts.netoos-linuxgithub.com/tiann/hapi
Codex: https://hapi.tail7733ee.ts.netoos-linuxhttps://github.com/tiann/hapi

Both models reproduce the doubled 7, 3, e, n, and o faithfully, repeatedly, in both prose and code. Through Cursor, the same model families gave us github.com/tian/…, os-linux, and a MagicDNS with a short digit-run.

Model held constant. The drop appears through Cursor and not when the model is driven directly.

On the “strawberry” framing

I believe ‘Strawberry’ is a counting task (how many r’s) - a hard character-level operation. Reproducing a string you were just given is not that tho - and your observation that “output exactly X” passes proves the model represents these characters fine. If this were the strawberry tokenization weakness, exact-copy would fail too. Since it doesn’t, I reckon that means the failure isn’t “the model can’t handle doubled characters”; it’s “under some condition, the string the model regenerates isn’t faithful to the one it was given.” That condition is what differs between native and Cursor.

Your suggested mitigations turned out to be clues

  • “Keep identifiers in a rule/AGENTS.md so they sit where the model generates.” I do - and my distance-recall test literally seeds the string in-context and asks the model to regenerate it. Native reproduces it 100/100. So “the string is in context” is not sufficient to cause the drop natively. What happens to that string inside Cursor’s context pipeline before the model generates from it?
  • “Higher reasoning-effort variants are less prone.” This is the tell. If a knob you control (effort / model-variant defaults) changes the drop rate, it isn’t an immutable tokenization limit - it’s a configuration outcome. Our native runs were default effort and still hit zero. That squarely implicates Cursor’s defaults.
  • copy-and-verify helps an operator, but it concedes the raw output isn’t trustworthy and can’t be applied to every sentence an agent emits.

Where I’d request your folks to have a look (given the model is constant)

  1. Context assembly / compaction - does the exact identifier survive byte-for-byte into the prompt the model actually sees at generation time, or is it summarized/truncated/re-tokenized first? A dropped doubled char is exactly what that loses.
  2. Scaffolding that suppresses “I don’t know.” Direct clue: native Claude abstains when it isn’t sure (“a guessed origin is worse than none”); Cursor-routed Claude confabulated a mangled string instead. That suggests your harness/system prompt pressures a confident, terse answer over abstention - turning “unsure” into a near-miss.
  3. Sampling defaults - confirm temperature/top-p/effort for your Claude and Codex routes match provider defaults; higher temperature increases near-miss substitution of rare tokens.
  4. Routing/version “drift” - is the routed model+version+tokenizer the one named in the UI?

The request

Run this exact distance-recall protocol inside Cursor (Composer, Auto, Claude, your Codex route), N≥8 each, and share your per-emission drop rate. I can send the harness and full data - just ask. If you come back at ~0 too, I’ll happily close this. If you don’t, the gap is in one of the four places above - and I’d genuinely like to fix the operator experience with you vs argue the point, it’s a constant annoyance on my side :frowning:

Thanks,
Gavin

Hey @mohitjain any feedback on this, I’ve done some work to narrow it down - I think there’s something to be done here. As is, it leaves me in a frustratingly bad spot, with constant errors being introduced, solely from the fact I’m using cursor vs claude or codex.