can1357/oh-my-pi · error · ToolError
Assertion failed
Error message
Assertion failed
What it means
Inside a cmux browser run, `assert(cond, text?)` is exposed in the run scope; when the condition is falsy it throws a ToolError with the provided text or the default "Assertion failed". It lets agent-run scripts encode expectations that abort the run on violation.
Source
Thrown at packages/coding-agent/src/tools/browser/cmux/cmux-tab.ts:1443
);
}
};
if (signal.aborted) onAbort();
else signal.addEventListener("abort", onAbort, { once: true });
try {
const runtime = tab.ensureRuntime(opts.snapshot);
// setCwd is non-exclusive; setRunScope/run still assert same-realm ownership.
// Keep both inside try so a concurrent in-process eval/browser run surfaces as
// a rejected promise the supervisor can report, never an unhandled rejection.
runtime.setCwd(opts.snapshot.cwd);
const runTab = bindRunFacade(tab, signal, rejectionOwner, recordFloatingFailure);
runtime.setRunScope({
page: bindRunFacade(tab.page, signal, rejectionOwner, recordFloatingFailure),
browser: bindRunFacade(tab.browser, signal, rejectionOwner, recordFloatingFailure),
tab: runTab,
assert: (cond: unknown, text?: string): void => {
if (!cond) throw new ToolError(text ?? "Assertion failed");
},
wait: (msOrPredicate: number | (() => unknown), waitOpts?: WaitPredicateOptions): Promise<unknown> =>
observeBrowserRunPromise(
waitForRun(
msOrPredicate,
signal,
typeof msOrPredicate === "number"
? waitOpts
: {
timeout: resolvePredicateTimeout(opts.timeoutMs, waitOpts?.timeout),
interval: waitOpts?.interval,
},
).catch(error => {
throw markBrowserRunRejection(error, rejectionOwner);
}),
rejectionOwner,
recordFloatingFailure,
),View on GitHub (pinned to 9690622007)
Solutions
- Add a descriptive message to every assert call so failures are diagnosable instead of the generic text.
- Check the asserted condition against the live page — use waitFor before asserting element existence.
- Beware falsy evaluate results (0, '', null): assert on the precise value (`=== expected`) rather than truthiness.
Example fix
// before
assert(rows.length);
// after
assert(rows.length > 0, `expected result rows, got ${rows.length}`); Defensive patterns
Strategy: try-catch
Validate before calling
const rows = await tab.evaluate(`document.querySelectorAll('.row').length`);
if (!(rows > 0)) await tab.waitFor('.row', { timeoutMs: 10000 }); Try / catch
try {
assert(rows.length > 0, `expected rows, got ${rows.length}`);
} catch (err) {
if (err instanceof ToolError && err.message === 'Assertion failed') {
// capture diagnostics (screenshot, current URL) before rethrowing
await tab.screenshot('assert-failure.png');
}
throw err;
} Prevention
- Always pass a descriptive message to assert().
- Wait for state before asserting it; assertions are not waits.
- Assert with strict comparisons to avoid falsy-value surprises (0, '', null).
When it happens
Trigger: Calling assert(...) in run-scope code with a falsy condition and no message: failed DOM checks, unexpected return values from evaluate, missing elements or state before proceeding.
Common situations: Assertions written against a page that changed structure; assert used where a wait was needed (element not yet present); truthiness bugs (assert on 0/''/null results from evaluate).
Related errors
- Assertion failed
- pr://${repo}/${parsed.number}/diff/${index} resolved to a mi
- tab.select() requires a <select> element
- Unsupported selector action ${action}
- tab.waitFor(${JSON.stringify(selector)}) timed out after ${t
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/78a6ad5d54ae9c4e.
Report an issue: GitHub.