Conversation
verifyState wrapped its loop in `catch: async (error) => debugLog(error)`. Any error inside an iteration was logged and the loop moved on, so a rate-limited or aborted model call left codeBlocks empty. The result was `totalAttempted === 0`, which verifyState reads as "no assertion could express this claim" — a verdict about the page, returned for an infrastructure failure, with a suggestion telling Tester to reword a claim that was never the problem. The catch also amplified the failure it hid: each iteration retried the model, so one claim cost verifyAttempts x the retries the provider already performs, all against the limit that had just been hit. And it ate the fatal browser errors action.attempt deliberately rethrows. loop() rethrows when no catch handler is given, and handles StopError before reaching one, so dropping the handler lets real failures out without disturbing the stop() calls. Both callers are ready for it: the verify tool reports `Verify tool failed: <error>` from its own catch, and Pilot guards its call with `.catch(() => null)`. inexpressible now means only what it says — the model answered without usable assertion code. Also make the cached early return match the declared return type; it omitted inexpressible and results, which `tsc --noCheck` never flagged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JCYmFZvUsENZBx71Yqojs6
…-failure # Conflicts: # CHANGELOG.md
DenysKuchma
approved these changes
Sep 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Found while investigating a failed Tester session (Langfuse trace
078285ca5b32d556be2695c50141c5c8, sessionVariableIncreasedBrown198). Around 6 of the run's 13 minutes went intoverify(), and three of the long ones returned "No assertion could express this claim" — for claims that passed minutes later.The bug
verifyStatewrapped its loop in a handler that swallowed everything:invokeConversationthrows, so a rate-limited or aborted model call landed there, was logged, and the loop moved on withcodeBlocksstill empty. That leavestotalAttempted === 0, which the next lines read as:So an infrastructure failure came back to Tester as a verdict about the page, with a suggestion pointing the wrong way: "This is not evidence the page is wrong — restate it." Tester duly reworded an assertion that was never the problem.
Two further costs:
verifyAttemptsx the retries the provider already performs — all against the token limit that had just been hit. In the trace, oneverify()produced 9 rate-limit errors and ran for 2m28s; another for 3m41s.action.attemptrethrows those deliberately; the handler caught them and carried on.The fix
Drop the handler.
loop()rethrows when none is given, and handlesStopErrorbefore it, so thestop()calls inverifyStateare unaffected. Both callers already cope:tools.ts— the verify tool's own catch reportsVerify tool failed: <error>afterthrowIfFatalBrowserErrorpilot.ts— guards its call with.catch(() => null)inexpressiblenow means only what it says: the model answered without usable assertion code.Also makes the cached early return match the declared return type — it omitted
inexpressibleandresults, whichtsc --noChecknever flagged.tsc --noEmitis clean on this file now.Tests
New
tests/unit/navigator-verify-failure.test.ts: a throwing model call must reject rather than resolve as inexpressible (fails onmain), and a model answer with no assertion code must still resolve as inexpressible. Existing navigator unit tests andtests/integration/pass.Related
Stacks naturally with #209, which fixes a separate cause of the same
inexpressiblesymptom.🤖 Generated with Claude Code
https://claude.ai/code/session_01JCYmFZvUsENZBx71Yqojs6