Most tools that claim to diagnose production failures are measured against bugs someone wrote on purpose. We wanted a harder test, so we took a bug that Prefect’s own engineers found and fixed in October 2026, put it back into the code, ran it until it genuinely crashed, and asked Tomosu to explain the crash with no knowledge that a fix existed.
Tomosu passed. Working only from a real crash log and the repository as it stood with the fix reverted, it named the right file and function, identified the right root cause, and proposed a fix that solves the same problem as the patch Prefect actually shipped in pull request #22761. It was not told that the bug was known, or which lines had changed.
- The bug:
pause_flow_run(timeout=-1)passed a negative timeout through to Python’stime.sleep(), which raisedValueError: sleep length must be non-negativedeep inside Prefect instead of rejecting the argument up front. - What Tomosu got right: the file (
src/prefect/flow_runs.py), the function (pause_flow_run), the root cause, and aValueErrorguard in the right place. - Where it fell short: its guard would raise a
TypeErrorwhentimeoutisNone, a case the real patch handles explicitly. - The lesson: the diagnosis is the valuable output. The suggested patch is a starting point for a reviewer, not something to merge unread.
- What did we actually test?
- What does “sleep length must be non-negative” mean here?
- How do you reproduce a fixed bug on purpose?
- What did Tomosu find from the log alone?
- How close was Tomosu’s fix to the real one?
- What does the one gap tell you?
- Why does a real bug beat a synthetic benchmark?
- How Tomosu does this on your repositories
- FAQ
What did we actually test?
A real-bug regression validation is a test in which a genuine, already-fixed defect is reintroduced into a real codebase, triggered for real, and handed to an analysis tool that has not been told what the right answer is. The point is to measure diagnosis rather than recall: a synthetic bug tells you whether a tool recognises a pattern someone planted, while a historical bug tells you whether it can reconstruct a conclusion that real engineers took real time to reach.
We chose Prefect because its history is public and auditable, and we chose PR #22761 because it is a clean candidate: one file, one well-isolated validation fix, reachable by calling a single function with a single bad argument. No scheduler internals, no worker pool, no timing window to win.
“fix: raise ValueError for negative timeout in pause_flow_run”, merged 16 October 2026 as commit df5b008. It changed src/prefect/flow_runs.py and tests/test_flow_runs.py, adding a guard to four functions, pause_flow_run, apause_flow_run, suspend_flow_run and asuspend_flow_run, plus four regression tests.
What does “sleep length must be non-negative” mean here?
It means a negative number reached Python’s time.sleep(), which rejects it at the interpreter level. Before the fix, Prefect’s pause and suspend functions accepted a timeout without validating it, and a negative value flowed quietly into an internal sleep call. The user’s mistake surfaced several frames away from the line that made it.
from prefect import flow
from prefect.flow_runs import pause_flow_run
@flow
def demo():
pause_flow_run(timeout=-1) # no validation: goes straight to time.sleep()
ValueError: sleep length must be non-negative
# raised inside Prefect’s internals, not at the call site
# file cited in the traceback: src/prefect/flow_runs.py
The fix Prefect shipped is the obvious one, stated up front rather than left to the interpreter:
if timeout is not None and timeout < 0:
raise ValueError(f"'timeout' must be a non-negative number, got {timeout}")
context = FlowRunContext.get()
How do you reproduce a fixed bug on purpose?
Carefully, and on a branch you intend to throw away. The discipline that makes the result meaningful is that only the fix is reverted: everything else stays current, so the tool is reading today’s codebase with one historical defect in it.
- 1Cloned PrefectHQ/prefect and confirmed the fork carries genuine upstream commit history, not a hand-built fixture.
- 2Picked PR #22761 as a clean, single-file candidate reachable from one function call.
- 3Reverted only the guard lines in
src/prefect/flow_runs.py, on a throwaway branch, leaving the rest of the tree untouched. - 4Wrote a six-line flow that calls
pause_flow_run(timeout=-1)from inside a real flow run, against a local Prefect server. - 5Ran it. The bug reproduced for real: Prefect crashed with
ValueError('sleep length must be non-negative')and wrote the full traceback into its own SQLite log store. - 6Pointed the Tomosu Unified Runner at that log store in Prefect mode, letting it extract the failing file, fetch the still-buggy source from GitHub, and diagnose it through the same pipeline that runs in production.
- 7Only then compared Tomosu’s diagnosis and suggested fix against the real diff.
What did Tomosu find from the log alone?
Given the crash log and the repository, and nothing else, the analysis pinpointed pause_flow_run in src/prefect/flow_runs.py and stated the root cause as an unvalidated negative timeout reaching time.sleep(). It classified the finding under log failure signatures, rule LOG-01, at severity error, with a confidence of 74%, and proposed concrete code.
if timeout < 0:
raise ValueError("timeout must be greater than or equal to 0")
if poll_interval <= 0:
raise ValueError("poll_interval must be greater than 0")
How close was Tomosu’s fix to the real one?
Close on everything that decides whether the bug is understood, and imperfect on the edges. Here is the comparison line by line.
| Aspect | Real fix, PR #22761 | Tomosu’s suggestion | Match |
|---|---|---|---|
| File and function | flow_runs.py, pause_flow_run | Same | Yes |
| Root cause | Negative timeout reaches time.sleep() | Same | Yes |
| Rejects a negative timeout | Yes, via ValueError | Yes, via ValueError | Yes |
Handles timeout=None | Yes, explicit is not None check | No, would raise TypeError | Gap |
poll_interval validation | Not included | Added, beyond the brief | Extra |
| Error message | Echoes the offending value back | Generic text, no value | Minor |
| Placement | Top of the function, before the context lookup | A few lines later, still before the crash point | Minor |
What does the one gap tell you?
That a suggested patch is a draft for a reviewer. timeout is optional in these functions, so None is a perfectly ordinary value; a bare if timeout < 0 would turn a valid call into a TypeError. A reviewer catches that in seconds. An automated merge would not.
This validation says Tomosu reconstructed the right diagnosis from a real log without being told the answer. It does not say the generated patch is merge-ready, and we are not claiming it is. One bug is also one data point: it supports the pipeline doing its job on this class of failure, not a precision or recall number.
Why does a real bug beat a synthetic benchmark?
Because a synthetic bug is written by someone who already knows what the right answer looks like, and that knowledge leaks into the test. The error message is tidy, the failing function is the one being examined, and nothing else in the repository is competing for attention. Real failures are not like that. This one surfaced several frames from the call that caused it, in a codebase of thousands of files, with a traceback that pointed at the victim rather than the culprit.
The bug was real, the crash was real, and the log was real. The only thing withheld was the answer.
That is also why the comparison is worth publishing in full, gap included. A validation that reports only the parts that went well is a marketing exercise, and it tells a reader nothing about where to apply judgement.
How Tomosu does this on your repositories
The pipeline used here is the one that runs in production, not a special evaluation mode. In normal use it works the same way: a failure arrives in your logs, Tomosu extracts the failing path, reads the code that is actually implicated, and returns a diagnosis with a severity, a rule and a confidence, so a reviewer starts from a located root cause instead of a stack trace.
- →Connect a repository and get a baseline reliability score, free and on private code: see the repository scan guide or start a scan.
- →Put the same signal on the pull request, so a reliability verdict arrives before a merge rather than after an incident. That is the merge gate.
- →Give your coding agents the same view through the MCP server, so the code they write is checked against the same standard you review by.
Key takeaways
- Tomosu re-derived a real, already-fixed Prefect defect from a real crash log, with the known fix withheld until the final comparison.
- It identified the right file, the right function and the right root cause, and proposed a
ValueErrorguard that solves the same problem as the shipped patch. - Its patch missed one edge case, an optional
timeoutofNone, and added apoll_intervalcheck the real fix does not include. - Treat a generated fix as a reviewer’s starting point. The located diagnosis is the part that saves the time.
- A historical bug from a public repository is a harder and more honest test than a synthetic one, because nobody wrote it to be found.
- One validated bug is a data point, not a hit rate. We publish the gap for the same reason we publish the result.
Frequently asked questions
What is a real-bug regression validation?
A real-bug regression validation takes a bug that was genuinely found and fixed in a real codebase, puts the bug back, runs it until it actually fails, and then asks an analysis tool to diagnose the failure with no knowledge of the known answer. Because the bug, the crash and the log are all real, the result measures diagnosis rather than pattern-matching against a benchmark someone wrote on purpose.
What does “ValueError: sleep length must be non-negative” mean in Prefect?
It means a negative number reached Python's time.sleep(). In the pre-fix Prefect code, pause_flow_run(timeout=-1) passed the negative timeout through to an internal sleep call, and Python rejected it at the interpreter level. The traceback points at Prefect's internals rather than at the line where the caller passed the bad argument, which is what makes the error confusing to read.
Which Prefect pull request was used for this test?
PrefectHQ/prefect #22761, “fix: raise ValueError for negative timeout in pause_flow_run”, merged on 16 October 2026 as commit df5b008. It adds a negative-timeout guard to four functions in src/prefect/flow_runs.py, pause_flow_run, apause_flow_run, suspend_flow_run and asuspend_flow_run, plus four regression tests.
Did Tomosu know the answer in advance?
No. Tomosu was given the crash log from the local Prefect server and the repository as it stood with the fix reverted. It was not told that an upstream fix existed, which pull request it was, or which lines had been changed. The comparison with the real patch was made afterwards, by us.
Was Tomosu's suggested fix identical to the real one?
No, and that matters. Tomosu identified the same file, the same function and the same root cause, and proposed a ValueError guard that solves it. But its guard would raise a TypeError when timeout is None, which the real patch handles with an explicit None check, and its error message does not echo the offending value back. It also proposed an extra guard on poll_interval that the real patch does not include.
What does the gap say about using AI suggestions at the merge gate?
That a suggested fix is a starting point for a reviewer, not a patch to merge unread. The useful output here is the diagnosis: the right file, the right function and the right root cause, reached from a log. A human still owns the edge cases, which in this case was a None timeout.
Can Tomosu do this on a private repository?
Yes. The log-analysis pipeline used here is the same one that runs in production, and connecting a repository is free on every plan, public or private. What is metered is the reliability work itself, counted in TCUs.
A diagnosis is worth more than an alert. This validation ran against the open-source PrefectHQ/prefect repository, using the throwaway branch tomosu-eval-22761 to stage the reverted file. The branch was deleted afterwards and nothing was changed on Prefect’s main branch.
Discuss the validation → · Questions: contact@tomosu.ai