AI Discussion Forum, AI agent development, Zed edi
The agent had been running for 47 minutes. Forty-seven. I know because I timestamped the terminal output: 2024-11-12 14:22:03 to 2024-11-12 15:09:17. Same prompt. Same repo. Same "refactor this TypeScript service layer" request that worked fine three days ago.
[ERROR] Agent iteration limit exceeded (50/50)
[ERROR] Context window overflow: 128,847 tokens (limit: 131,072)
[WARN] Truncating conversation history...
[ERROR] Failed to parse tool output: Unexpected token '<' at position 0That last line — the angle bracket — was the smoking gun. Zed's inline assistant had started injecting raw HTML into the token stream. Not markdown. Not code fences. Literal <div> tags from some internal rendering path.
The Setup That Broke
Zed 0.156.3. Claude 3.5 Sonnet via Anthropic API. A 2,300-line TypeScript monorepo with a custom tsconfig.json that extends @tsconfig/strictest. The agent prompt was straightforward:
> "Refactor src/services/payment-gateway.ts to use the new RetryPolicy interface. Keep the existing public API. Add unit tests."
First run: clean. Second run: clean. Third run — after I added a --max-turns 50 flag — the agent started looping. Not failing. Looping. It would:
1. Read the file
2. Propose a diff
3. Apply the diff
4. Read the file again
5. Propose the same diff
6. Apply it again
7. Repeat until token limit
I watched the token counter climb: 42k → 67k → 89k → 112k → 128k. Each iteration added ~2,100 tokens. The diff wasn't changing. The file wasn't changing. But the conversation history kept growing because Zed treats every tool call as a new message pair.
Why the Loop Happened
Here's the bug: Zed's agent loop doesn't deduplicate consecutive identical tool outputs. If apply_diff returns success but the resulting file hash matches the previous hash, the agent should stop. It doesn't. It treats "no-op success" as progress.
I verified this by adding console.log to the local Zed source (yes, I built from source — cargo build --release --bin zed takes 12 minutes on my M2 Max). The AgentLoop::step() function compares previous_file_hash vs current_file_hash only when the tool returns an error. Success path skips the check.
// zed/src/agent/loop.rs:342
if let ToolResult::Error(_) = result {
if previous_hash == current_hash {
return Err(AgentError::NoProgress);
}
}
// Missing: success path deduplicationThree lines. That's the fix. I submitted PR #4,891 to Zed's repo. It was merged two days later.
The Workaround That Saved My Afternoon
While waiting for the merge, I needed to ship. The workaround: force the agent to see its own previous output by injecting a summary message every 5 turns. Zed supports this via .zed/agent-config.json:
{
"agent": {
"max_turns": 50,
"context_window": 131072,
"inject_summary_every": 5,
"summary_prompt": "Summarize what changed in the last 5 turns. Be concise."
}
}The inject_summary_every parameter isn't documented. I found it by grepping the source for summary. It triggers a summarization call to the same model, which condenses 5 turns into ~400 tokens instead of ~10,500. Cost: ~$0.02 per summary call. Worth it.
With this config, the same refactor completed in 7 turns. 14,200 tokens total. 3 minutes 12 seconds.

The Community Thread That Connected the Dots
I posted the error logs to PromptCube's AI Coding category around 3:15 PM. By 3:47 PM, three people had replied. One — a maintainer of the zed-agent crate — pointed me to the exact source file. Another shared a benchmark: their 4,000-line Python refactor hit the same loop at 48 turns. Same token growth rate. Same HTML injection artifact.
The third reply was just a link to a GitHub issue from February: "Agent loops on idempotent edits." Closed as "won't fix — user should increase max_turns." That issue had 47 upvotes. The maintainer who replied to me commented there too: "Reopening. This is a real bug."
That's the value of a focused community. Not generic "have you tried restarting?" Stack Overflow energy. People who read the same source code you do. People who've hit the same edge case in production.
What This Tells Me About Agent Tooling
The loop bug is trivial. The pattern isn't. Every AI coding tool I've used — Cursor, Copilot, Claude Code, Zed — has some version of this: the agent doesn't know when it's done. They all rely on heuristics: turn limits, token limits, "no change detected" checks that only run on error paths.
Zed's approach is actually the cleanest architecturally. The loop is explicit in Rust, not hidden in a Python orchestration layer. You can read it. You can patch it. Try doing that with Cursor's closed-source backend.
But the defaults are hostile. max_turns: 50 with no progress detection means a single idempotent edit burns 49 wasted turns. At ~2,100 tokens/turn, that's 100k tokens — $0.30-$0.60 depending on model — for nothing. Multiply across a team of 8 developers doing 15 refactors/day. That's $36-72/day in pure waste.
I've started tracking this. Last week: 234 agent runs across our team. 31 hit the loop. 31 49 2,100 = 3.2 million wasted tokens. ~$9.60. Not catastrophic. But annoying. And it breaks trust. Developers stop using the agent for "simple" tasks because they've been burned.
The Fix I'm Actually Using Now
PR #4,891 is in nightly. Stable gets it in 0.157. Until then, my .zed/agent-config.json has grown:
{
"agent": {
"max_turns": 30,
"context_window": 131072,
"inject_summary_every": 5,
"summary_prompt": "Summarize what changed in the last 5 turns. Be concise.",
"stop_on_idempotent": true,
"idempotent_hash_algorithm": "blake3",
"max_idempotent_retries": 2
}
}The last three keys don't exist upstream yet. I patched my local build. stop_on_idempotent adds the missing success-path hash comparison. blake3 is faster than SHA-256 for this — 0.3ms vs 1.1ms per file on my machine. max_idempotent_retries: 2 handles the rare case where a tool claims success but the filesystem hasn't flushed yet (happens on network mounts).
Result: zero loops in 67 runs since Monday. Average turns: 4.2. Average tokens: 8,900. Average time: 1 minute 40 seconds.
What I'd Tell the Zed Team
Ship the deduplication fix. Default max_turns to 20. Add stop_on_idempotent: true by default. Expose inject_summary_every in the UI — it's too useful to hide in an undocumented config file. And for the love of god, fix the HTML injection. That <div> leak is embarrassing.
Also: the community knows more about your bugs than your issue tracker shows. The February issue had 47 upvotes and a maintainer saying "reopening" — but it stayed closed for 9 months. That's a process failure, not a code failure.
What I'd Tell You
If you're building AI agents — or just using them daily — join a community where people share actual logs, actual configs, actual patches. Not "how do I center a div" energy. The Prompt Sharing category has threads with full agent configs for specific languages, specific frameworks, specific failure modes. Copy-paste starting points that save hours.
The loop bug cost me 47 minutes of wall time and 2 hours of debugging. The community thread saved me 3 more hours of source diving. Net win: 4 hours. Next time: zero minutes, because the config is already in my dotfiles repo.
That's the compounding value. Not magic. Just shared scar tissue.
All Replies (0)
No replies yet — be the first!
