{"record":{"id":"79c3a4b8517d4212","repo":"can1357/oh-my-pi","slug":"label-cannot-run-this-handle-was-invalidated-a","errorCode":null,"errorMessage":"${label} cannot run: this handle was invalidated after ${state.invalidatedBy} timed out; run tab.observe() or tab.ariaSnapshot() to resolve a fresh handle","messagePattern":"(.+?) cannot run: this handle was invalidated after (.+?) timed out; run tab\\.observe\\(\\) or tab\\.ariaSnapshot\\(\\) to resolve a fresh handle","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/browser/tab-worker.ts","lineNumber":385,"sourceCode":"\ttype: ElementHandle[\"type\"];\n\tinvalidatedBy?: string;\n}\n\n/** Symbol-keyed original methods travel with each cached handle without enumerating or colliding. */\nconst RAW_HANDLE_METHODS = Symbol(\"browser.rawHandleMethods\");\n\ntype HandleWithRawMethods = ActionableHandle & { [RAW_HANDLE_METHODS]?: RawHandleMethods };\n\nasync function runGuardedHandleAction<T>(\n\thandle: ElementHandle,\n\tstate: RawHandleMethods,\n\tlabel: string,\n\tsignal: AbortSignal,\n\taction: () => Promise<T>,\n\tinvalidate?: () => Promise<void>,\n): Promise<T> {\n\tif (state.invalidatedBy) {\n\t\tthrow new ToolError(\n\t\t\t`${label} cannot run: this handle was invalidated after ${state.invalidatedBy} timed out; ` +\n\t\t\t\t\"run tab.observe() or tab.ariaSnapshot() to resolve a fresh handle\",\n\t\t);\n\t}\n\tthrowIfAborted(signal);\n\tconst pending = action();\n\ttry {\n\t\treturn await untilAborted(signal, () => pending);\n\t} catch (error) {\n\t\tif (!signal.aborted) throw error;\n\t\tstate.invalidatedBy = label;\n\t\tvoid pending.catch(() => undefined);\n\t\tawait withTimeout(\n\t\t\tPromise.all([handle.dispose().catch(() => undefined), invalidate?.().catch(() => undefined)]),\n\t\t\tHANDLE_ACTION_INVALIDATION_TIMEOUT_MS,\n\t\t\t`Timed out invalidating ${label}`,\n\t\t).catch(() => undefined);\n\t\tthrow error;","sourceCodeStart":367,"sourceCodeEnd":403,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/browser/tab-worker.ts#L367-L403","documentation":"Element handles tracked by the tab worker can be invalidated when an operation on them times out. runGuardedHandleAction checks `state.invalidatedBy` before running any action; if a previous operation timed out and invalidated the handle, every subsequent action on that stale handle throws this error, telling you to re-observe.","triggerScenarios":"Holding a handle from tab.observe()/tab.id() across a slow page update, letting an action on it time out (setting `invalidatedBy`), then calling another action (click/fill) on the same handle id.","commonSituations":"Pages that re-render or navigate between observe and act, making handles stale; long hangs on a click that poison the handle for later calls; agent loops reusing old handle ids after a timeout.","solutions":["Run `tab.observe()` or `tab.ariaSnapshot()` to resolve a fresh handle, then retry the action with the new id.","Retry promptly after observing — handles go stale when the DOM changes again.","Avoid reusing handle ids across multiple steps; resolve, act, discard.","If timeouts are the root cause, make the selector more specific or wait for the element to be actionable before acting."],"exampleFix":"// before\ntab.click(handleId); // handleId invalidated by earlier timeout\n// after\nconst obs = await tab.observe();\nconst fresh = obs.find(el => el.role === \"button\" && el.name === \"Submit\");\nawait tab.click(fresh.id);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await tab.click(handleId);\n} catch (err) {\n  if (err instanceof ToolError && err.message.includes(\"was invalidated\")) {\n    const fresh = (await tab.observe()).find(el => el.name === targetName);\n    await tab.click(fresh.id);\n  } else throw err;\n}","preventionTips":["Resolve a fresh handle immediately before each action; don't cache handle ids","After any timeout, re-observe before retrying","Avoid actions on pages known to re-render between observe and act"],"tags":["handle","timeout","stale-element","browser-tool"],"backgroundTag":"stale-element-handle","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}