{"record":{"id":"ba03f2319fdee8e9","repo":"can1357/oh-my-pi","slug":"unknown-aria-ref-json-stringify-ref-run-tab-a","errorCode":null,"errorMessage":"Unknown ARIA ref ${JSON.stringify(ref)}. Run tab.ariaSnapshot() to refresh refs (they renumber each snapshot).","messagePattern":"Unknown ARIA ref (.+?)\\. Run tab\\.ariaSnapshot\\(\\) to refresh refs \\(they renumber each snapshot\\)\\.","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/browser/tab-worker.ts","lineNumber":2142,"sourceCode":"\t\ttry {\n\t\t\tconst isConnected = (await handle.evaluate(el => el.isConnected)) as boolean;\n\t\t\tif (!isConnected) {\n\t\t\t\tthis.#clearElementCache();\n\t\t\t\tthrow new ToolError(`Element id ${id} is stale. Run tab.observe() again.`);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (err instanceof ToolError) throw err;\n\t\t\tthis.#clearElementCache();\n\t\t\tthrow new ToolError(`Element id ${id} is stale. Run tab.observe() again.`);\n\t\t}\n\t\treturn handle;\n\t}\n\n\tasync #resolveAriaRef(id: string): Promise<ElementHandle> {\n\t\tconst ref = parseAriaRefSelector(id) ?? id.trim();\n\t\tconst handle = await resolveAriaRefHandle(this.#requirePage(), ref);\n\t\tif (!handle) {\n\t\t\tthrow new ToolError(\n\t\t\t\t`Unknown ARIA ref ${JSON.stringify(ref)}. Run tab.ariaSnapshot() to refresh refs (they renumber each snapshot).`,\n\t\t\t);\n\t\t}\n\t\treturn handle;\n\t}\n\n\t/**\n\t * Resolve a selector to an ElementHandle for handle-based actions. An\n\t * `aria-ref=eN` selector resolves against the latest ariaSnapshot's refs\n\t * (main world); anything else goes through the normal locator wait.\n\t */\n\tasync #resolveActionHandle(selector: string, timeoutMs: number, sig: AbortSignal): Promise<ElementHandle> {\n\t\tif (parseAriaRefSelector(selector) !== null) return this.#resolveAriaRef(selector);\n\t\treturn (await untilAborted(sig, () =>\n\t\t\tthis.#requirePage().locator(normalizeSelector(selector)).setTimeout(timeoutMs).waitHandle({ signal: sig }),\n\t\t)) as ElementHandle;\n\t}\n\t#clearElementCache(): void {","sourceCodeStart":2124,"sourceCodeEnd":2160,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/browser/tab-worker.ts#L2124-L2160","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nawait tab.click({ ref: 'e5' }); // from an old snapshot\n// after\nconst snap = await tab.ariaSnapshot();\nawait tab.click({ ref: snap.findRef('Submit') ?? 'e1' });","handlingStrategy":"validation","validationCode":"const ref = parseAriaRefSelector(id);\nif (!ref || !/^[A-Za-z0-9_-]+$/.test(String(ref))) throw new Error('invalid aria ref');","typeGuard":"function isAriaRef(v: unknown): v is string {\n\treturn typeof v === 'string' && v.trim().length > 0;\n}","tryCatchPattern":"try {\n\tawait act({ ref });\n} catch (err) {\n\tif (err instanceof ToolError && /Unknown ARIA ref/.test(err.message)) {\n\t\tconst snap = await tab.ariaSnapshot();\n\t\tref = remapRef(snap, ref); // refresh and remap\n\t\tawait act({ ref });\n\t} else throw err;\n}","preventionTips":["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"],"tags":["browser","aria-snapshot","stale-ref"],"backgroundTag":"stale-element-reference","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}