can1357/oh-my-pi · error · ToolError

${label} threw a JavaScript exception:\n${value.__ompErr}

Error message

${label} threw a JavaScript exception:\n${value.__ompErr}

What it means

`unwrapEvalEnvelope` inspects values returned by cmux eval RPCs. The in-page wrapper serializes a thrown exception as `{ __ompErr: "<stack/message>" }`; when seen, this function rethrows it as a ToolError `<label> threw a JavaScript exception:` followed by the original error text. It converts silent in-page JS failures into actionable host-side errors for `evaluate`/`evaluateOnSelector`.

Source

Thrown at packages/coding-agent/src/tools/browser/cmux/rpc.ts:160

			const __v = (${expr});
			if (__v && typeof __v.then === "function") return { __ompPromise: true };
			return { __ompOk: __v === undefined ? null : __v };
		} catch (e) {
			return { __ompErr: (e && (e.stack || e.message)) || String(e) };
		}
	})()`;
}

/**
 * Decode a {@link serializeEvalWithEnvelope} result: rethrow page-side
 * exceptions as rich {@link ToolError}s, reject unserializable Promise
 * returns with an actionable message, and pass through values from daemons
 * that did not run the wrapper.
 */
export function unwrapEvalEnvelope<TResult>(value: unknown, label: string): TResult {
	if (value && typeof value === "object") {
		if ("__ompErr" in value && typeof value.__ompErr === "string") {
			throw new ToolError(`${label} threw a JavaScript exception:\n${value.__ompErr}`);
		}
		if ("__ompPromise" in value && value.__ompPromise === true) {
			throw new ToolError(
				`${label} returned a Promise, but this surface evaluates synchronously and cannot await it — return a plain value (poll with waitForFunction for async state instead)`,
			);
		}
		if ("__ompOk" in value) {
			return value.__ompOk as TResult;
		}
	}
	return value as TResult;
}

export function mapWaitUntil(waitUntil: string | undefined): "interactive" | "complete" {
	return waitUntil === "domcontentloaded" ? "interactive" : "complete";
}

export interface ResolveCmuxKindOptions {

View on GitHub (pinned to 9690622007)

Solutions

  1. Read the embedded __ompErr text — it names the exact line/exception in your script; fix the script accordingly.
  2. Null-check DOM results inside the script: `const el = document.querySelector(sel); if (!el) return null;`.
  3. Validate the script's syntax (it's a string — an editor/lint pass catches typos) and prefer evaluateOnSelector for element work instead of manual querySelector.

Example fix

// before
tab.evaluate(`document.querySelector('.price').textContent`);
// after
tab.evaluate(`(() => { const el = document.querySelector('.price'); return el ? el.textContent : null; })()`);
Defensive patterns

Strategy: try-catch

Validate before calling

// validate script syntax before sending
new Function(script); // throws SyntaxError locally if the script string is malformed

Try / catch

try {
  const price = await tab.evaluate(script);
} catch (err) {
  if (err instanceof ToolError && err.message.includes('threw a JavaScript exception')) {
    console.error('in-page script failed:', err.message.split('\n').slice(1).join('\n'));
    // fall back to a null-safe variant of the script
  }
  throw err;
}

Prevention

When it happens

Trigger: The script passed to tab.evaluate/evaluateOnSelector throws in the page: TypeError on null querySelector results, syntax errors, reference errors, or calling page-only APIs incorrectly.

Common situations: Selector matched nothing and the script dereferences the result; using browser APIs unavailable in the page context; JSON-injected script literals with quoting issues breaking syntax; page CSP or hydration timing.

Related errors


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