Build1 publisher3 min readPublished
A cached empty list made a read-back commit rule ask for 70 paragraphs
The engineering lead at app platform GoodBarber traced 70 duplicate paragraphs to a read path serving a 742-byte empty list out of a 60-second cache. His debugging order now puts the prompt last.
The Engineer · Build desk

What happened
- A runtime whose commit rule required a read-back to confirm every write created 70 paragraphs on one draft article, asking the model for another each time the read-back came back empty.
- The server bypasses the cache for exactly one read, the one immediately following a write, and a list call in between resets that exemption.
- Of ten incidents the author could date on his own systems since April, the model was the direct author of one, while he had reached for the model first in four of them.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- cost A failing read-back does not halt the loop, it pays for the next attempt: 69 unnecessary paragraph generations went through before the cache expired, plus the cleanup on a live draft.
- constraint A read-back commit rule inherits the authority of the read path underneath it, and behind a 60-second cache it becomes a retry generator.
- exposure Any agent on this server that creates an article without setting status has published it, and the delete it later issues returns nothing identifying the object, so no reviewer can reconstruct what went live.
A read-back is a confirmation only when the read answers from storage. The runtime's rule was that nothing counts as done until a read-back confirms it [2], and the paragraph list it read back was served from a cache with a 60-second lifetime [5]. So the loop wrote a paragraph, asked whether the paragraph existed, was told no, and asked the model for another one [3]. The response sizes are the diagnosis: 742 bytes, 742, 742, twenty-five times in a row, then 131,991 bytes in a single response [4]. Twenty-five identical answers inside a 60-second window puts the read interval at roughly 2.4 seconds or less, which is 60 divided by 25 [30]. The task wanted one paragraph, so 69 model calls went to retries against a cached empty list [29]. The same read path got measured on September 3, 2026: 50 cycles on a test app, every article created as a draft and deleted afterwards [9]. In create, read, delete, read, the immediate read after the delete still returned the object 3 times in 50, and that is after August's fix [10]. Once in 50, the read straight after a create did not find the new article at all [11]. Then the sequence an agent actually produces, create, read, read, delete, list, read: the deleted object came back 50 times out of 50 and stayed for a median 61.1 seconds, maximum 61.2 [12]. The exemption explains the behaviour. The server bypasses the cache for exactly one read, the one that follows the write, and a list in between resets it [13]. An agent that lists before it verifies has already used the only authoritative read it was going to get. One of the three questions in the post's debugging order is how long the error lived [28], and here the answer is a median of 61.1 seconds, just past the 60-second lifetime given for the paragraph cache [31]. None of this reads as a failure in the transcript. "A transcript of that agent would show a delete with a 200 and a read that returned the article," wrote the post's author, who runs engineering at GoodBarber and operates a production MCP server plus several scheduled agents [14][8]. His rule for reading one: "When an agent has been wrong, the transcript is the first thing you open and the last thing you should trust" [21]. What it can answer is what was called with which arguments, not whether the call worked [22]. The delete response would not settle it either, since it carries the policy envelope only, with no deleted flag, no id and no status [15]. The ordering rule he takes from all this is "attribute before you touch the prompt" [18]: data and credentials first, then tool and cache, then harness and scheduler, then the model, then the human reading the output [19]. Verifiability sets that order. Every layer above the model is deterministic and checkable in minutes, and the prompt comes last because it is the only layer where a fix cannot be verified [20]. On his own reflex he is blunt about why the model got blamed first: "It was the last thing that had produced output, and blaming it costs nothing" [7]. The counting underneath is thin, and he says so. Ten incidents on his own systems since April, coded by the layer that was actually wrong, and the model is the direct author of one of them, the invented tool names, where a client let it retry the same name until it gave up [23]. The model was his first suspect in four of the ten [24]. He notes the list is what he could date, not a sample, and that the split describes the list, not his fleet [25].
What to watch
- Whether cms_create_article's default flips from published to draft, since a create without an explicit status currently publishes.
- Whether the delete response starts carrying an id and a status, so a transcript can show what was removed.
- Whether the 3-in-50 post-delete read leak survives the next fix after August's.