can1357/oh-my-pi · error · Error

Element handle selector no longer resolves

Error message

Element handle selector no longer resolves

What it means

CmuxElementHandle invokes actions by re-resolving its stored selector inside the page (findElement(spec)); when the selector no longer matches any element in the live DOM, the page-side script throws 'Element handle selector no longer resolves', which surfaces as this ToolError. It means the element the handle was created from has been removed, re-rendered, or the selector text no longer matches.

Source

Thrown at packages/coding-agent/src/tools/browser/cmux/cmux-tab.ts:806

	}

	async elementExists(selector: string): Promise<boolean> {
		return await this.#selectorExists(this.#selectorSpec(selector));
	}

	async elementBox(selector: string): Promise<BoundingBox | null> {
		return await this.#selectorBox(this.#selectorSpec(selector));
	}

	async evaluateOnSelector<TResult>(selector: string, source: string, args: unknown[]): Promise<TResult> {
		const spec = this.#selectorSpec(selector);
		const script = `(() => {
			const spec = ${JSON.stringify(spec)};
			const source = ${JSON.stringify(source)};
			const args = ${JSON.stringify(args)};
			${PAGE_SELECTOR_HELPERS}
			const element = findElement(spec);
			if (!element) throw new Error("Element handle selector no longer resolves");
			const callable = (0, eval)("(" + source + ")");
			return callable(element, ...args);
		})()`;
		// Envelope so a stale selector or a throwing callback reports its actual
		// error instead of the daemon's generic js_error (see tab.evaluate()).
		const result = (await this.#request("browser.eval", {
			script: serializeEvalWithEnvelope(script, []),
		})) as CmuxEvalResult;
		return unwrapEvalEnvelope<TResult>(result.value, "elementHandle.evaluate()");
	}

	async pageContent(): Promise<string> {
		return await this.#evalScript<string>("document.documentElement.outerHTML");
	}

	async pageScreenshot(opts: ScreenshotOptions = {}): Promise<Buffer | string> {
		if (opts.selector) await this.scrollIntoView(opts.selector);
		const result = await this.#captureScreenshotPng(this.#runContext?.timeoutMs ?? 30_000);

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-acquire the handle right before use: call tab.waitForSelector(sel) again instead of reusing the old CmuxElementHandle.
  2. Minimize the gap between obtaining the handle and acting on it — keep actions on the same element adjacent in code.
  3. Use a more stable selector (data-testid, id, semantic role) so re-resolution survives re-renders.
  4. If the page navigates or mutates heavily, perform the action inside tab.evaluate() against a freshly queried element.

Example fix

// before: stale handle reused after re-render
const btn = await tab.waitForSelector("button.submit");
await someAsyncWork();
await btn.click(); // selector no longer resolves
// after: re-resolve just before acting
await someAsyncWork();
const btn = await tab.waitForSelector("button.submit");
await btn.click();
Defensive patterns

Strategy: retry

Validate before calling

// re-verify the selector still resolves before using the handle
const stillThere = await tab.evaluate(
  (sel) => !!document.querySelector(sel),
  selector,
);
if (!stillThere) await tab.waitForSelector(selector);

Try / catch

try {
  await elementHandle.click();
} catch (err) {
  if (err instanceof ToolError && err.message.includes("no longer resolves")) {
    const fresh = await tab.waitForSelector(selector, { timeout: 10_000 });
    await fresh.click();
  } else throw err;
}

Prevention

When it happens

Trigger: Using a CmuxElementHandle obtained via tab.waitFor()/tab.waitForSelector() after a framework re-render replaced the node; the DOM subtree was torn down (route change, modal close); the handle's selector relied on nth-position text/structure that changed; the page navigated away.

Common situations: React/Vue/Svelte keyed re-renders that recreate elements between waitFor and the action; handles held across await points while an animation swaps nodes; stale handles reused after waitForNavigation; auto-generated classes changing between renders when the selector was built from them.

Related errors


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