can1357/oh-my-pi · error · ToolError
Unknown ARIA ref ${JSON.stringify(ref)}. Run tab.ariaSnapsho
Error message
Unknown ARIA ref ${JSON.stringify(ref)}. Run tab.ariaSnapshot() to refresh refs (they renumber each snapshot). What it means
ARIA refs produced by tab.ariaSnapshot() are resolved against the live page; the ref numbering is only valid for that snapshot. When resolveAriaRefHandle cannot find an element matching the given ref (the snapshot is outdated or the ref was never issued), this ToolError is thrown and points the caller at ariaSnapshot() to regenerate refs.
Source
Thrown at packages/coding-agent/src/tools/browser/tab-worker.ts:2142
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) {
throw new ToolError(
`Unknown ARIA ref ${JSON.stringify(ref)}. Run tab.ariaSnapshot() to refresh refs (they renumber each snapshot).`,
);
}
return handle;
}
/**
* Resolve a selector to an ElementHandle for handle-based actions. An
* `aria-ref=eN` selector resolves against the latest ariaSnapshot's refs
* (main world); anything else goes through the normal locator wait.
*/
async #resolveActionHandle(selector: string, timeoutMs: number, sig: AbortSignal): Promise<ElementHandle> {
if (parseAriaRefSelector(selector) !== null) return this.#resolveAriaRef(selector);
return (await untilAborted(sig, () =>
this.#requirePage().locator(normalizeSelector(selector)).setTimeout(timeoutMs).waitHandle({ signal: sig }),
)) as ElementHandle;
}
#clearElementCache(): void {View on GitHub (pinned to 9690622007)
Solutions
- Run tab.ariaSnapshot() to refresh refs, then use the newly issued ref
- Re-snapshot after any action that mutates the page before referencing elements again
- Verify the ref comes from the same tab/snapshot you are operating on
- If refs churn rapidly, interact via tab.observe() element ids or selectors instead
Example fix
// before
await tab.click({ ref: 'e5' }); // from an old snapshot
// after
const snap = await tab.ariaSnapshot();
await tab.click({ ref: snap.findRef('Submit') ?? 'e1' }); Defensive patterns
Strategy: validation
Validate before calling
const ref = parseAriaRefSelector(id);
if (!ref || !/^[A-Za-z0-9_-]+$/.test(String(ref))) throw new Error('invalid aria ref'); Type guard
function isAriaRef(v: unknown): v is string {
return typeof v === 'string' && v.trim().length > 0;
} Try / catch
try {
await act({ ref });
} catch (err) {
if (err instanceof ToolError && /Unknown ARIA ref/.test(err.message)) {
const snap = await tab.ariaSnapshot();
ref = remapRef(snap, ref); // refresh and remap
await act({ ref });
} else throw err;
} Prevention
- Refresh ariaSnapshot() after every page mutation — refs renumber
- Always take refs from the most recent snapshot of the same tab
- Validate ref format before sending
- Prefer fresh observation over stored refs for dynamic pages
When it happens
Trigger: Calling an action with a ref from an older ariaSnapshot() after the page changed (refs renumber each snapshot); passing a malformed or arbitrary string as the ref; snapshot expired after DOM mutations.
Common situations: Agent snapshots once and reuses refs across multiple steps; page content is dynamic so refs shift between snapshot and action; ref string typo or copied from a different tab.
Related errors
- Browser selector must be a string; got ${kind}. tab.click/ty
- tab.ariaSnapshot: selector ${__sel} matched no element
- Timed out waiting for CDP endpoint ${cdpUrl}${lastStatus !==
- No page targets available on the attached browser
- No page target matched ${JSON.stringify(options.matcher)}. A
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/ba03f2319fdee8e9.
Report an issue: GitHub.