{"record":{"id":"348adbcee246dbb0","repo":"jackwener/OpenCLI","slug":"no-element-found-matching-selector-query","errorCode":null,"errorMessage":"No element found matching selector: ${query}","messagePattern":"No element found matching selector: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"extension/src/cdp.ts","lineNumber":362,"sourceCode":"export async function setFileInputFiles(\n  tabId: number,\n  files: string[],\n  selector?: string,\n): Promise<void> {\n  await ensureAttached(tabId);\n\n  // Enable DOM + Page domains. Page is needed for file-chooser interception.\n  await sendDebuggerCommand({ tabId }, 'DOM.enable');\n  await sendDebuggerCommand({ tabId }, 'Page.enable');\n\n  // Find the file input element (used to trigger the chooser).\n  const query = selector || 'input[type=\"file\"]';\n  const found = await sendDebuggerCommand({ tabId }, 'Runtime.evaluate', {\n    expression: `!!document.querySelector(${JSON.stringify(query)})`,\n    returnByValue: true,\n  }) as { result?: { value?: boolean } };\n  if (!found.result?.value) {\n    throw new Error(`No element found matching selector: ${query}`);\n  }\n\n  // Chrome rejects DOM.setFileInputFiles with a plain nodeId/backendNodeId when\n  // the debugger is attached via chrome.debugger (crbug 928255, \"-32000 Not\n  // allowed\"). The only accepted path is file-chooser interception: enable it,\n  // programmatically open the chooser, and use the backendNodeId that the\n  // intercepted Page.fileChooserOpened event hands back. See issue #2108.\n  await sendDebuggerCommand({ tabId }, 'Page.setInterceptFileChooserDialog', { enabled: true });\n  try {\n    const backendNodeId = await new Promise<number>((resolve, reject) => {\n      const timer = setTimeout(() => {\n        cleanup();\n        reject(new Error('Page.fileChooserOpened not received within 5s — the input may not have opened a file chooser'));\n      }, 5000);\n      const listener = (source: chrome.debugger.Debuggee, method: string, params: unknown) => {\n        if (source.tabId !== tabId || method !== 'Page.fileChooserOpened') return;\n        // This is our chooser event — settle now either way, so a malformed\n        // event rejects immediately instead of hanging until the 5s timeout.","sourceCodeStart":344,"sourceCodeEnd":380,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/extension/src/cdp.ts#L344-L380","documentation":"setFileInputFiles first probes the page with Runtime.evaluate to confirm the selector matches an element; if document.querySelector finds nothing, it throws 'No element found matching selector'. This fails fast before attempting the file-chooser interception dance required by chrome.debugger attachments.","triggerScenarios":"handleSetFileInput is called with a selector (default 'input[type=\"file\"]') that matches no element in the current DOM of the attached tab.","commonSituations":"The file input is inside a shadow DOM or iframe that document.querySelector can't see; the page hasn't rendered the uploader yet; selector typo; the input is only added after clicking an 'Upload' button.","solutions":["Verify the selector matches in DevTools console: document.querySelector('…')","Wait for the upload UI to render before calling setFileInputFiles","Click the button that injects the file input first, then set the files","Target the correct frame if the input lives in an iframe (the probe runs in the main frame)","Use the default selector by passing an empty selector only when exactly one file input exists"],"exampleFix":"// before\nawait cdp.setFileInputFiles(tabId, files, 'input.upload');   // input not yet in DOM\n// after\nawait cdp.evaluate(tabId, \"!!document.querySelector('input.upload') || document.querySelector('#add')?.click()\");\nawait new Promise(r => setTimeout(r, 500));   // let it render\nawait cdp.setFileInputFiles(tabId, files, 'input.upload');","handlingStrategy":"validation","validationCode":"const found = await cdp.evaluate(tabId, `!!document.querySelector(${JSON.stringify(selector)})`);\nif (found !== true) throw new Error(`pre-check failed: ${selector} not in DOM`);","typeGuard":"function selectorExists(v: unknown): v is true {\n  return v === true;\n}","tryCatchPattern":"try {\n  await cdp.setFileInputFiles(tabId, files, selector);\n} catch (e) {\n  if ((e as Error).message.startsWith('No element found matching selector')) {\n    await waitForSelector(tabId, selector);\n    return retry();\n  }\n  throw e;\n}","preventionTips":["Wait for the upload UI to render before setting files","Click the trigger button that injects the input first","Account for shadow DOM/iframes — the probe only sees the main frame document","Verify selectors in DevTools console on the exact page state","Prefer stable ids/data attributes over generated class names"],"tags":["cdp","dom","selector","file-upload"],"backgroundTag":"element-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}