{"record":{"id":"cf78d156ca1f0cb5","repo":"jackwener/OpenCLI","slug":"element-not-found-saferef","errorCode":null,"errorMessage":"Element not found: ${safeRef}","messagePattern":"Element not found: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/browser/page.ts","lineNumber":403,"sourceCode":"\n      // Try getBoxModel\n      try {\n        const box = await this.cdp('DOM.getBoxModel', { nodeId }) as { model: { content: number[] } };\n        if (box.model?.content) {\n          const c = box.model.content;\n          const cx = (c[0] + c[2] + c[4] + c[6]) / 4;\n          const cy = (c[1] + c[3] + c[5] + c[7]) / 4;\n          await this.nativeClick(Math.round(cx), Math.round(cy));\n          return;\n        }\n      } catch { /* fallthrough */ }\n    } catch { /* fallthrough */ }\n\n    // Final fallback: regular click\n    await this.evaluate(`\n      (() => {\n        const el = document.querySelector('[data-opencli-ref=\"' + ${safeRef} + '\"]');\n        if (!el) throw new Error('Element not found: ' + ${safeRef});\n        el.click();\n        return 'clicked';\n      })()\n    `);\n  }\n\n}\n","sourceCodeStart":385,"sourceCodeEnd":411,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/src/browser/page.ts#L385-L411","documentation":"As the final fallback in clickWithQuads, an in-page evaluate() queries the element by its data-opencli-ref and throws 'Element not found: <ref>' if the ref no longer resolves in the live document.","triggerScenarios":"clickWithQuads reached the evaluate fallback (earlier strategies failed) and the ref attribute no longer exists — the page re-rendered or navigated after the snapshot was captured.","commonSituations":"SPAs re-rendering lists and regenerating refs; navigation triggered by a prior click; long-lived sessions where snapshots are minutes old.","solutions":["Re-take the DOM snapshot and retry with a fresh ref","Perform navigation/re-render then re-resolve the element before clicking","Add a wait/retry loop that re-snapshots on ref-not-found"],"exampleFix":"// before\nawait page.clickWithQuads(staleRef, ...);\n// after\ntry { await page.clickWithQuads(staleRef, ...); }\ncatch { const snap = await page.snapshot(); await page.clickWithQuads(snap.refFor('Submit'), ...); }","handlingStrategy":"retry","validationCode":"const exists = await page.evaluate(`!!document.querySelector('[data-opencli-ref=\"${ref}\"]')`);\nif (!exists) await page.snapshot(); // refresh before retry","typeGuard":"function refResolves(found: Element | null): found is Element { return found !== null; }","tryCatchPattern":"try { await page.clickWithQuads(ref, ...); }\ncatch (e) {\n  if (String(e.message).startsWith('Element not found')) {\n    const snap = await page.snapshot();\n    await page.clickWithQuads(snap.refFor(label), ...);\n  } else throw e;\n}","preventionTips":["Re-snapshot after SPA re-renders","Avoid long delays between snapshot and click","Expect ref invalidation after navigation"],"tags":["dom","stale-reference","browser-automation"],"backgroundTag":"stale-element-reference","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}