Product
Platform Agentic Code Scanner Unified Runner Tomosu Agents Indexes Solutions
Get Started
Scan your repo VS Code plugin Tomosu MCP GitHub App CodeRabbit FAQ
Resources
Case Studies Governance Impact TCO Calculator Download / Free Trial
Pricing
Plans and pricing
Company
About Blog News Contact Request Demo → Try Free Log in GitHub
Case study · Regression validation · Prefect

We put a real Prefect bug back.
Tomosu found it from the logs.

Tomosu AI·7 min read·

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.

Quick answer

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.

Bug under test
PR #22761, merged 16 Oct 2026
Method
Revert the fix, reproduce, diagnose blind
Result
Pass, with one documented gap
On this page
  1. What did we actually test?
  2. What does “sleep length must be non-negative” mean here?
  3. How do you reproduce a fixed bug on purpose?
  4. What did Tomosu find from the log alone?
  5. How close was Tomosu’s fix to the real one?
  6. What does the one gap tell you?
  7. Why does a real bug beat a synthetic benchmark?
  8. How Tomosu does this on your repositories
  9. 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.

The pull request under test

“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.

The six lines that trigger itpre-fix behaviour
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()
What Prefect loggedreal crash, real log store
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:

src/prefect/flow_runs.py, PR #22761the real fix
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.

The real-bug regression validation loop Five stages left to right: revert the shipped fix, reproduce the real crash, capture the log, diagnose blind with Tomosu, compare against the real patch. Revert THE SHIPPED FIX Reproduce A REAL CRASH Capture THE LOG STORE Diagnose BLIND Compare VS PATCH NOTHING IN STAGES 1 TO 4 KNOWS THE ANSWER THE KNOWN FIX ENTERS ONLY AT THE LAST STAGE
Figure 1. The known answer is withheld until the final comparison, which is what makes the result a diagnosis rather than a lookup.

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.

Exact
Localisation
right file and right function
Correct
Root cause
negative timeout reaches time.sleep()
74%
Confidence
rule LOG-01, severity error
1
Gap found
None timeout not handled
Tomosu’s suggested fixproposed blind
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.

AspectReal fix, PR #22761Tomosu’s suggestionMatch
File and functionflow_runs.py, pause_flow_runSameYes
Root causeNegative timeout reaches time.sleep()SameYes
Rejects a negative timeoutYes, via ValueErrorYes, via ValueErrorYes
Handles timeout=NoneYes, explicit is not None checkNo, would raise TypeErrorGap
poll_interval validationNot includedAdded, beyond the briefExtra
Error messageEchoes the offending value backGeneric text, no valueMinor
PlacementTop of the function, before the context lookupA few lines later, still before the crash pointMinor

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.

Read the result precisely

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.

Key takeaways

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

Request Demo →