{"record":{"id":"f45ae44434fe4ddb","repo":"can1357/oh-my-pi","slug":"timed-out-clicking-selector-seen-lastseen-m","errorCode":null,"errorMessage":"Timed out clicking ${selector} (seen ${lastSeen} matches; last reason: ${lastReason ?? \"unknown\"}). If there are multiple matching elements, use observe + tab.id() or a more specific selector.","messagePattern":"Timed out clicking (.+?) \\(seen (.+?) matches; last reason: (.+?)\\)\\. If there are multiple matching elements, use observe \\+ tab\\.id\\(\\) or a more specific selector\\.","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/browser/tab-worker.ts","lineNumber":888,"sourceCode":"\t\t\t}\n\t\t\tconst actionability = await isClickActionable(target);\n\t\t\tif (!actionability.ok) {\n\t\t\t\tlastReason = actionability.reason;\n\t\t\t\tawait untilAborted(clickSignal, () => Bun.sleep(100));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait untilAborted(clickSignal, () => target.click());\n\t\t\t\treturn;\n\t\t\t} catch (err) {\n\t\t\t\tlastReason = err instanceof Error ? err.message : String(err);\n\t\t\t\tawait untilAborted(clickSignal, () => Bun.sleep(100));\n\t\t\t}\n\t\t} finally {\n\t\t\tawait Promise.all(handles.map(async handle => handle.dispose().catch(() => undefined)));\n\t\t}\n\t}\n\tthrow new ToolError(\n\t\t`Timed out clicking ${selector} (seen ${lastSeen} matches; last reason: ${lastReason ?? \"unknown\"}). ` +\n\t\t\t\"If there are multiple matching elements, use observe + tab.id() or a more specific selector.\",\n\t);\n}\n\n/**\n * Hint appended to a selector op's fail-fast timeout, given the selector's current\n * match count: a missing element (consent wall, wrong page) reads differently from\n * a present-but-unactionable one.\n */\nexport function formatSelectorMatchHint(count: number): string {\n\treturn count === 0\n\t\t? \"; selector currently matches no elements — run tab.observe() or tab.ariaSnapshot() to inspect the page\"\n\t\t: `; selector currently matches ${count} element(s) but the action never became possible — the element may be hidden or covered (try tab.scrollIntoView() or a more specific selector)`;\n}\n\nexport interface InflightOp {\n\tlabel: string;","sourceCodeStart":870,"sourceCodeEnd":906,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/browser/tab-worker.ts#L870-L906","documentation":"The click loop resolves candidate handles for the selector and retries until an overall timeout; each attempt records why the click was skipped. If the loop exhausts the timeout without a successful click, this error reports how many matches were seen and the last skip reason, and suggests observe + tab.id() or a more specific selector.","triggerScenarios":"Selector matching multiple elements where earlier matches are non-clickable (hidden, covered, disabled, detached) so every attempt is skipped; selector matching zero elements; element becomes visible only after the timeout.","commonSituations":"Cookie banners with several similarly-named buttons; elements behind overlays; SPA where the target element mounts slowly; ambiguous selectors like `text/OK` matching many buttons.","solutions":["Read the `last reason` in the message (e.g. not visible, intercepted, detached) and address that specific condition.","Use a more specific selector: prefer `aria/<name>` or `text/<exact label>` over broad CSS.","Run `tab.observe()` and click by handle id (`tab.id()`) to disambiguate multiple matches.","Wait for the element to be actionable before clicking, or increase the action timeout."],"exampleFix":"// before\ntab.click(\"text/Submit\"); // matches 3 buttons, none clickable\n// after\nconst obs = await tab.observe();\nconst btn = obs.find(el => el.name === \"Submit order\");\ntab.click(`aria/${btn.name}`);","handlingStrategy":"validation","validationCode":"const matches = await tab.handles(selector);\nif (matches.length > 1) {\n  const obs = await tab.observe();\n  throw new Error(`selector '${selector}' matched ${matches.length} elements; pick one via observe`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await tab.click(selector);\n} catch (err) {\n  if (err instanceof ToolError && err.message.startsWith(\"Timed out clicking\")) {\n    const obs = await tab.observe();\n    const target = obs.find(el => el.clickable && el.name === expectedLabel);\n    await tab.click(target.id);\n  } else throw err;\n}","preventionTips":["Prefer aria/<exact name> over broad text/CSS selectors","Check the 'last reason' in the timeout message before retrying blindly","Disambiguate multi-match selectors via observe + tab.id()"],"tags":["click","timeout","selector","browser-tool"],"backgroundTag":"click-timeout-no-actionable-element","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}