{"record":{"id":"b0e88174238959e0","repo":"didi/DoKit","slug":"can-t-select-any-node-with-this-selector-select","errorCode":null,"errorMessage":"Can't select any node with this selector: ${selector(path)}","messagePattern":"Can't select any node with this selector: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"Web/packages/core/src/common/js/finder/index.js","lineNumber":137,"sourceCode":"    if (node.level === level - 1) {\n      query = `${path[i].name} > ${query}`;\n    } else {\n      query = `${path[i].name} ${query}`;\n    }\n    node = path[i];\n  }\n  return query;\n}\nfunction penalty(path) {\n  return path.map(node => node.penalty).reduce((acc, i) => acc + i, 0);\n}\nfunction unique(path) {\n  const s = selector(path)\n  const len = rootDocument.querySelectorAll(s).length\n  // console.log('[unique] selector:', s, 'len:', len)\n  switch (len) {\n    case 0:\n      throw new Error(`Can't select any node with this selector: ${selector(path)}`);\n    case 1:\n      return true;\n    default:\n      return false;\n  }\n}\nfunction id(input) {\n  const elementId = input.getAttribute('id');\n  if (elementId && config.idName(elementId)) {\n    return {\n      name: `#${cssesc(elementId, { isIdentifier: true })}`,\n      penalty: 0,\n      type: 'id',\n    };\n  }\n  return null;\n}\nfunction attr(input) {","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/didi/DoKit/blob/626827cddb2feb2f3aee87a52a064b4e5ca2bed4/Web/packages/core/src/common/js/finder/index.js#L119-L155","documentation":"Thrown inside unique() when a candidate selector, built from a candidate path, matches zero nodes when run via rootDocument.querySelectorAll. The algorithm assumes a selector derived from a live element must match at least that element; matching nothing means the generated selector is invalid or the document being queried is not the document that contains the element.","triggerScenarios":"rootDocument (resolved from options.root) differs from the element's actual ownerDocument — e.g. element lives in an iframe but root is the main document, or vice versa. Also triggered by selectors containing characters that cssesc escapes differently across contexts, or when the DOM mutates between path construction and the querySelectorAll validation call.","commonSituations":"Element-picker / recording tools that capture elements in iframes or dynamically re-rendered React/Vue trees. The default root is document.body of the main document, so any iframe content breaks the invariant. Rapid DOM mutation between capture and selector generation.","solutions":["Set options.root (and thus rootDocument) to a node inside the same document as the target element, e.g. the iframe's document.body","Re-fetch the element and generate the selector synchronously in the same task, avoiding async gaps where the DOM can mutate","Wrap the finder call in try-catch and retry once after a microtask/frame, or fall back to an XPath or index-based locator","If running in a jsdom/test environment, ensure the element was created from the same jsdom document instance"],"exampleFix":"// before\nconst sel = finder(iframeEl, { root: document.body }); // wrong document\n\n// after\nconst iframeDoc = iframeEl.ownerDocument;\nconst sel = finder(iframeEl, { root: iframeDoc.body });","handlingStrategy":"validation","validationCode":"const root = el.ownerDocument.body;\nif (root.contains(el)) { const sel = finder(el, { root }); }","typeGuard":null,"tryCatchPattern":"try { sel = finder(el, opts); } catch (e) { if (/Can't select any node/.test(e.message)) { sel = buildManualLocator(el); /* e.g. data-* attribute path */ } else throw e; }","preventionTips":["Set options.root inside the same document (iframe/shadow) as the target element","Avoid async gaps between element capture and selector generation"],"tags":["dom","css-selector","iframe","queryselectorall","javascript"],"backgroundTag":null,"analyzedSha":"626827cddb2feb2f3aee87a52a064b4e5ca2bed4","analyzedAt":"2026-08-14T12:45:58.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}