[KongchangAI]
· 2 min read· 1,498 words

Coder and Reviewer Agents Stuck in an Infinite Loop: Why Step Limits Fail

Coder and Reviewer Agents Stuck in an Infinite Loop: Why Step Limits Fail

Step limits can't stop content-repetition loops; payload fingerprint detection is a lightweight, essential defense.

A developer's multi-agent loop ran all night as Coder and Reviewer exchanged identical messages without triggering step limits. The article explains why step counters can't detect byte-level content repetition, and proposes content fingerprinting—hashing message payloads and monitoring repetition and ABAB patterns in ~15 lines of code. It warns of the JSON sort_keys=True trap that silently disables detection.

A Simple Loop Experiment That Turned Into a Three-Day Standoff

A developer recently set up a classic multi-agent collaboration loop: the Coder agent writes code, the Reviewer agent gives feedback, and the Coder revises based on that feedback. He configured the workflow and went to sleep.

The next morning, the two agents were still talking. No crash, no error—just a continuous "conversation" in which the Coder kept fixing the same problem and the Reviewer kept raising the same suggestion. Both followed their instructions to the letter.

What finally ended it was the developer manually killing the process.

Coder agent and Reviewer agent looping

This seemingly absurd case exposes a widely overlooked flaw in current multi-agent frameworks.

Why Step Limits Can't Stop This Kind of Loop

Nearly every mainstream agent framework ships with a step limit built in. LangGraph, CrewAI, and the framework this developer built himself all have this mechanism. But it didn't work.

The reason lies in the perspective of the step counter: message 47 is a brand-new step because message 46 has finished executing and produced output, so the counter increments normally. The counter cannot recognize that message 47 is byte-for-byte identical to message 45.

In other words, a step limit can stop agents that never stop, but it cannot stop agents that finish every step normally and then immediately restart with identical content.

From the counter's perspective, every step is legitimate, new, and produces output. It has no ability to judge content repetition.

This is the heart of the problem: a step limit is a quantitative metric, whereas the essence of a loop is content repetition. Using quantity to detect a content-level problem is simply pointing in the wrong direction.

In multi-agent frameworks, the step limit is fundamentally a resource quota mechanism. Its original design intent is to prevent agents from falling into infinite tool calls or endlessly waiting for model responses—not to detect semantic-level repetitive behavior. In LangGraph, this parameter is usually called recursion_limit; in CrewAI, it manifests as max_iter or max_rpm. Their shared assumption is: as long as every step produces new output, the process is progressing. But this assumption doesn't hold in multi-agent dialogue scenarios—two agents can efficiently burn tokens forever in a closed loop of "raise a question → give the same answer → raise the same question again," while every step passes the counter's check perfectly. Such loops are often called livelocks in distributed systems: the system isn't deadlocked, the processes are still active, but the overall state no longer advances. Understanding the difference between livelock and deadlock is key to understanding why step limits are wrong in their very direction.

Replacing Step Counting with Content Fingerprinting

The developer made a key shift: stop counting steps, start counting fingerprints.

Specifically: hash each message and store the hash in a small window per session. If the same hash appears too many times, declare a loop. If a short alternating pattern like A→B→A→B appears, declare a ping-pong loop.

The entire detection logic takes only about fifteen lines of code.

The elegance of this approach is that it targets the very essence of loops—repetitive content. No matter how many steps separate messages between two agents, as long as the content fingerprints match, the loop can be caught.

At the core of content fingerprinting is a hash function, typically MD5, SHA-1, or lighter options like xxhash and fnv1a. Hashing the message payload passed between agents yields a fixed-length digest string: identical content always produces an identical hash, and a single differing character produces a completely different one. In practice, the detector maintains a sliding window or count dictionary (such as Python's collections.Counter or a fixed-length deque) recording the hashes of the most recent N messages. When a hash's frequency exceeds a threshold (e.g., the same fingerprint appears 3 times within 10 steps), or a period-2 alternating pattern like ABAB is detected, termination is triggered. The time complexity is O(1) per step and memory overhead is minimal, making it ideal for production monitoring paths. Note that hash collisions are nearly negligible in this context, but hash stability depends heavily on the serialization process—which is exactly the key-order problem discussed below.

A Silent Killer: The Key-Order Problem

Fingerprint detection has an easy-to-fall-into pitfall: once you're caught by it, the entire mechanism fails completely.

Before hashing a message, you must first serialize the payload to a string. That's where the problem lies: Python's json.dumps preserves a dictionary's insertion order by default.

{"a": 1, "b": 2} and {"b": 2, "a": 1} are semantically the same dictionary, but serialization produces two different strings, and thus two different hashes. To the detector, these are two completely different messages.

The result: loop detection never triggers even once. And on the surface, the system appears to be running perfectly—you'll have a hard time spotting it.

The fix is as simple as one line: add sort_keys=True during serialization. But if you don't know this detail, you'll write, ship, and deploy the entire system while the detection module does nothing from start to finish.

The dictionary key-order problem became especially insidious after Python 3.7, when dict officially began guaranteeing insertion-order iteration—before that, order was an implementation detail. This means the same logical data constructed via different code paths (e.g., agent A writes the tool field before content, while agent B does the reverse) will serialize differently. Similar pitfalls appear in floating-point precision, timezone representation, and Unicode normalization (NFC vs. NFD forms). The standard engineering approach is: perform a normalized serialization before hashing—sort_keys=True, separators=(',', ':') to strip whitespace, ensure_ascii=False for unified encoding—then feed it into the hash function. Some teams simply store the normalized JSON as the canonical form of the message, eliminating divergence at the source. This "normalize-then-compare" idea appears in Git's object model, content-addressable storage (CAS), and blockchain Merkle trees.

Where Fingerprint Detection's Boundaries Lie

The developer laid out an honest list of scenarios this approach cannot cover:

  • Different words, same meaning: messages that are semantically identical but differently worded cannot be recognized by hashes—embedding vectors are needed.
  • Every round is a brand-new message: this falls under what step limits should handle, not loop detection.
  • Malformed messages crashing downstream agents: this is a schema-level issue, unrelated to loop detection.

What fingerprint detection precisely captures is only one case: two agents repeatedly telling each other the same thing until someone notices. And that is exactly the most expensive kind of loop.

Semantic repetition detection is the next level of loop identification, typically relying on embedding vectors plus cosine similarity. The approach is to encode each message into a dense vector using a sentence-embedding model (such as sentence-transformers or OpenAI text-embedding-3-small), then compute the similarity between adjacent messages—declaring a semantic loop when similarity stays above a threshold (e.g., 0.95). Compared to exact hashing, embeddings can catch "different words, same meaning," but at the cost of calling an embedding model every step, significantly raising cost and latency, with threshold tuning itself being unstable. A more advanced route is to have a judge model (LLM-as-judge) periodically review the last several rounds to determine whether the conversation is spinning in place. The common compromise in practice is layered defense: the first layer uses hashes as a zero-cost filter, the second layer uses embeddings to catch what slips through, and the third layer brings in humans when costs spiral. No single method can cover all loop patterns—which is one reason multi-agent reliability remains an open problem to this day.

The Production Reality

If you're running a multi-agent system in production without payload fingerprint detection, then what you have isn't loop detection—it's a step limit, or a bill.

Hash your payload messages, monitor for duplicates, monitor for loop patterns, and trigger termination early. The advice is simple, but it was bought with three days of agent standoff and a great deal of burned tokens.

The reliability of multi-agent collaboration won't resolve itself as model capabilities improve—it must be confronted at the engineering level. Content fingerprint detection is one lightweight but essential line of defense.

One-line Summary

Step limits can't stop content-repetition loops; payload fingerprint detection is a lightweight, essential line of defense.

Paragraph Summary

A developer's multi-agent collaboration loop ran unattended all night as the Coder and Reviewer repeatedly exchanged the same messages without ever hitting a step limit. The article dissects why: step counters only see that every step completes legitimately and can't recognize byte-level content repetition. The solution is to switch to content fingerprints—hashing each message payload and monitoring repetition frequency and ABAB alternating patterns, achievable in about fifteen lines of code. The article specifically warns about the JSON serialization key-order trap: omitting sort_keys=True makes the same dictionary produce different hashes, silently breaking detection. Finally, it honestly maps out the boundaries of fingerprinting, noting that scenarios like "different words, same meaning" require embeddings to fill the gap, and stressing that in production, without payload fingerprints you're left with only a step limit or a bill.

#agent loop detection#multi-agent frameworks#content fingerprinting#step limit failure#JSON serialization key order#LangGraph recursion_limit#livelock vs deadlock
Share:

Related articles