{"record":{"id":"43ad07f45de30fd7","repo":"jackwener/OpenCLI","slug":"dom-node-not-found","errorCode":null,"errorMessage":"DOM node not found","messagePattern":"DOM node not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/browser/page.ts","lineNumber":370,"sourceCode":"\n    // Scroll element into view first\n    await this.evaluate(`\n      (() => {\n        const el = document.querySelector('[data-opencli-ref=\"' + ${safeRef} + '\"]');\n        if (el) el.scrollIntoView({ behavior: 'instant', block: 'center' });\n        return !!el;\n      })()\n    `);\n\n    try {\n      // Find DOM node via CDP\n      const doc = await this.cdp('DOM.getDocument', {}) as { root: { nodeId: number } };\n      const result = await this.cdp('DOM.querySelectorAll', {\n        nodeId: doc.root.nodeId,\n        selector: cssSelector,\n      }) as { nodeIds: number[] };\n\n      if (!result.nodeIds?.length) throw new Error('DOM node not found');\n\n      const nodeId = result.nodeIds[0];\n\n      // Try getContentQuads first (precise for inline elements)\n      try {\n        const quads = await this.cdp('DOM.getContentQuads', { nodeId }) as { quads: number[][] };\n        if (quads.quads?.length) {\n          const q = quads.quads[0];\n          const cx = (q[0] + q[2] + q[4] + q[6]) / 4;\n          const cy = (q[1] + q[3] + q[5] + q[7]) / 4;\n          await this.nativeClick(Math.round(cx), Math.round(cy));\n          return;\n        }\n      } catch { /* fallthrough */ }\n\n      // Try getBoxModel\n      try {\n        const box = await this.cdp('DOM.getBoxModel', { nodeId }) as { model: { content: number[] } };","sourceCodeStart":352,"sourceCodeEnd":388,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/src/browser/page.ts#L352-L388","documentation":"clickWithQuads resolves the CSS selector via CDP DOM.querySelectorAll against the document. If no matching nodes are returned it throws 'DOM node not found' before attempting quad-based clicking.","triggerScenarios":"Calling page.clickWithQuads(selector, ...) with a CSS selector that matches zero elements in the current document at CDP level.","commonSituations":"Selector typo; element rendered after an async load/spinner; element inside an iframe or shadow DOM not covered by document.querySelectorAll; page navigated between snapshot and click.","solutions":["Verify the selector matches in the page console: document.querySelectorAll(sel).length","Wait for the element to appear (e.g. waitForSelector/polling) before clicking","Use a snapshot ref instead of a raw CSS selector","Ensure the element is not inside a shadow root or iframe the CDP document query misses"],"exampleFix":"// before\nawait page.clickWithQuads('.submit-btn', ...);\n// after\nawait page.waitForSelector('.submit-btn');\nawait page.clickWithQuads('.submit-btn', ...);","handlingStrategy":"validation","validationCode":"const n = await page.evaluate(`document.querySelectorAll(${JSON.stringify(sel)}).length`);\nif (!n) throw new Error(`selector ${sel} matches nothing`);","typeGuard":"function hasNodes(r: { nodeIds?: number[] } | null): r is { nodeIds: number[] } {\n  return Array.isArray(r?.nodeIds) && r.nodeIds.length > 0;\n}","tryCatchPattern":"try { await page.clickWithQuads(sel, ...); }\ncatch (e) {\n  if (e.message === 'DOM node not found') {\n    await page.waitForSelector(sel);\n    await page.clickWithQuads(sel, ...);\n  } else throw e;\n}","preventionTips":["Wait for element presence before clicking","Validate selectors in the page console","Avoid selectors targeting iframe/shadow-DOM content for document-level queries"],"tags":["dom","selector","cdp","browser-automation"],"backgroundTag":"element-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}