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

  1. Add a descriptive message to every assert call so failures are diagnosable instead of the generic text.
  2. Check the asserted condition against the live page — use waitFor before asserting element existence.
  3. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/78a6ad5d54ae9c4e. Report an issue: GitHub.