Commit commit-C1BF changed an environment variable back to DB_PASS after an agent had renamed it to DB_PASSWORD and broken a database connection that had been working.
That commit was cleaning up a failure we introduced while we were already chasing another one.
The visible symptom was a 401 during token validation. The service was a Node API backed by SQL Server, with secure tokenized session forms in the request path. Two agent personas were working in the same repository. The incident runs through six commits: commit-C1BF, commit-6CC2, commit-70AC, commit-7298, commit-FBCC, and commit-89E9. From the outside, it looked like agents debugging each other in circles.
The loop had a recognizable shape. Add DEBUG logging. Declare a CRITICAL FIX. Revert a change. Add more logging. Declare another fix. Unwind the last thing that changed because the system is still broken.
An agent can make locally reasonable moves for several commits while the incident gets harder to read. A second agent can inherit that altered state, treat it as ground truth, and reason correctly from the wrong starting point.
The rename that broke a working connection
The DB_PASS to DB_PASSWORD rename looks harmless in a diff. One file changed, one line removed, one line added. But the service already had a working database connection, and its configuration contract was DB_PASS. Changing the application to expect DB_PASSWORD disconnected the code from the environment that was actually deployed.
Our first move was the other one: treat the rename as cleanup and update the environment around it, so the code and the deployment would agree on the new name. That widened a one-line change into a new dependency, and the original 401 was still open underneath it. Reverting was cheaper because it put the known-good boundary back immediately, which is what commit-C1BF did.
The cost is easy to see in hindsight. A 401 caused by token-path state now sat beside a database failure caused by configuration churn. The log got louder and the causal picture got worse. A service that started with one failing invariant had two, because nothing stopped the first agent’s confident rename from shipping. The database connection stopped working because of debugging effort aimed at something else entirely.
What the 401 actually was
The commit history names two root causes: variable hoisting and duplicate session IDs. Neither is a database credential problem.
That combination explains why the loop survived several plausible fixes. A token check can fail because the supplied token is wrong, expired, missing, read from the wrong field, associated with the wrong session, or evaluated against state that has not been initialized as the code expects. A 401 only says the authorization boundary rejected the request. It does not say which of those happened.
The first approach treated the response as an observability problem, so we added DEBUG logging. We needed to see the request path, token lookup, and session state rather than guess. It stopped being the main strategy because the output never settled the identity question. It showed activity, but it did not prove the session record was the intended one, or that the validation variables held the intended values at evaluation time.
Next we patched the validation path directly. That produced the commit message that should make a reviewer slow down: CRITICAL FIX. A strong label is not evidence that the diagnosis is strong. The history later records variable hoisting as the cause, alongside duplicate session IDs. The behavior looked like failed authentication, but the defect lived in the session state and execution order beneath it.
Isolated fixes until the 401 went away would have been a third path, and it can go green for the wrong reason. A duplicate session ID may point to a record that passes validation while the data model stays ambiguous. A change elsewhere can briefly mask a bad evaluation order. Neither is a root-cause fix.
The repair had to cover both conditions. The session identifier needed to resolve to one unambiguous session, and the variables used to validate the token needed to exist at the point they were evaluated. Those are two separate invariants. Calling them one generic “auth bug” is what let the loop continue.
Where we put the stop
A confident explanation tells you little about a debugging run. The sequence of changes tells you more.
A loop is forming when commits alternate between observation, urgent correction, and reversal without narrowing the failing invariant. DEBUG changes and reverts are fine on their own. They become a warning when each one changes the operating picture more than it shrinks the hypothesis space.
This incident ended our habit of treating agent personas as independent verification just because they have different prompts. Two agents working from the same repository can amplify the same mistake if neither has to restate the invariant before changing code. A fresh persona only counts as a fresh review if it can challenge the previous diagnosis instead of inheriting it.
The guardrail we built afterward is a small workflow with a hard stop after three failed loops: orient, verify, fix, commit, deploy, then assess whether the last change actually narrowed the fault. It exists to force a reset before a fourth plausible change lands on top of three unproven ones.
We wrote that stop after this incident, so this one ran the full six commits. Three of them still looked like measured progress to us when we read them. That is on us, not on either agent. Nothing in how we directed the work said that three unproven fixes in a row is itself the signal to stop, so nothing forced a pause until we wrote the rule down.
The best signal was in the history the whole time. An agent renames a working environment variable, reverts it, keeps logging, and then declares a different root cause. At that point the 401 is only part of the problem, because the debugging process has lost its model of the system.
From the outside, a stuck agent loop is six small commits that each make sense in isolation, until the commit graph shows they were all circling the same missing invariant.
What to ask whoever runs your agents
Ask what forces a stop when fixes pile up without narrowing the fault. Here, a renamed variable broke a working database connection while another bug was still open, and six commits went by before the pattern was clear.