can1357/oh-my-pi · error · ToolError
Unknown element id ${id}. Run tab.observe() to refresh the e
Error message
Unknown element id ${id}. Run tab.observe() to refresh the element list. What it means
Interactions can reference cached elements by numeric id from the last observe(). #resolveCachedHandle looks the id up in #elementCache; an unknown id (never observed, out of range, or cache cleared by a new observe) throws this ToolError telling the user to re-observe. The cache is not auto-refreshed because element lists can be large and refreshes are expensive.
Source
Thrown at packages/coding-agent/src/tools/browser/tab-worker.ts:2123
async #waitForResponse(
pattern: string | RegExp | ((response: HTTPResponse) => boolean | Promise<boolean>),
timeout: number,
signal: AbortSignal,
): Promise<HTTPResponse> {
const page = this.#requirePage();
const predicate: (response: HTTPResponse) => boolean | Promise<boolean> =
typeof pattern === "function"
? pattern
: pattern instanceof RegExp
? response => pattern.test(response.url())
: response => response.url().includes(pattern);
return (await untilAborted(signal, () => page.waitForResponse(predicate, { timeout, signal }))) as HTTPResponse;
}
async #resolveCachedHandle(id: number): Promise<ElementHandle> {
const handle = this.#elementCache.get(id);
if (!handle) throw new ToolError(`Unknown element id ${id}. Run tab.observe() to refresh the element list.`);
try {
const isConnected = (await handle.evaluate(el => el.isConnected)) as boolean;
if (!isConnected) {
this.#clearElementCache();
throw new ToolError(`Element id ${id} is stale. Run tab.observe() again.`);
}
} catch (err) {
if (err instanceof ToolError) throw err;
this.#clearElementCache();
throw new ToolError(`Element id ${id} is stale. Run tab.observe() again.`);
}
return handle;
}
async #resolveAriaRef(id: string): Promise<ElementHandle> {
const ref = parseAriaRefSelector(id) ?? id.trim();
const handle = await resolveAriaRefHandle(this.#requirePage(), ref);
if (!handle) {View on GitHub (pinned to 9690622007)
Solutions
- Run tab.observe() to get the current element list, then use ids from that fresh output.
- Retry the action with the corrected id — check the id is in the latest observe result.
- If observing frequently invalidates ids, snapshot the needed selector/aria-ref strings instead of ids.
- Avoid interleaving observes from multiple consumers on the same tab.
Example fix
// before
await tab.click(42); // id 42 from an older observe
// after
const { elements } = await tab.observe();
const target = elements.find(e => e.name === "Submit");
await tab.click(target.id); Defensive patterns
Strategy: try-catch
Validate before calling
const known = latestObservation.elements.some(e => e.id === id);
if (!known) throw new Error(`id ${id} not in latest observe() output; re-observe first`); Try / catch
try {
await tab.click(id);
} catch (err) {
if (err.message.startsWith("Unknown element id")) {
const fresh = await tab.observe();
const el = fresh.elements.find(e => e.name === expectedName);
if (el) return tab.click(el.id);
}
throw err;
} Prevention
- Always use ids from the most recent observe() output
- Never reuse ids across observes or tabs
- Re-observe after any navigation or heavy DOM change
- Store selectors/refs instead of ids for long-lived automation
When it happens
Trigger: Calling an action with an element id that was never returned by tab.observe(), an id beyond the current list, or an id invalidated because another observe() cleared and rebuilt #elementCache.
Common situations: LLM/agent hallucinating an id or reusing ids from a previous observe after the list shifted; two clients sharing one tab where each observe() invalidates the other's ids; ids from a different tab's observation.
Related errors
- Element id ${id} is stale. Run tab.observe() again.
- bun pm cache failed: ${cacheResult.stderr}
- bun pm cache returned an empty cache path
- Failed to refresh Bun's git cache for ${source.host}/${sourc
- Invalid marketplace name for cache: "${marketplace}"
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/ab483b15688f6fc3.
Report an issue: GitHub.