{"record":{"id":"48a3bc9bfd66dbf4","repo":"can1357/oh-my-pi","slug":"tab-uploadfile-requires-an-input-type-file-e","errorCode":null,"errorMessage":"tab.uploadFile() requires an <input type=\"file\"> element (got <${tagName.toLowerCase()}>)","messagePattern":"tab\\.uploadFile\\(\\) requires an <input type=\"file\"> element \\(got <(.+?)>\\)","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/browser/tab-worker.ts","lineNumber":2077,"sourceCode":"\t}\n\n\tasync #uploadFile(\n\t\tselector: string,\n\t\tfilePaths: string[],\n\t\ttimeoutMs: number,\n\t\tsignal: AbortSignal,\n\t\tsession: SessionSnapshot,\n\t): Promise<void> {\n\t\tif (!filePaths.length) throw new ToolError(\"tab.uploadFile() requires at least one file path\");\n\t\tconst handle = await this.#resolveActionHandle(selector, timeoutMs, signal);\n\t\ttry {\n\t\t\tconst absolute = filePaths.map(filePath => resolveToCwd(filePath, session.cwd));\n\t\t\tconst upload = handle as unknown as { uploadFile: (...paths: string[]) => Promise<void> };\n\t\t\tconst tagName = (await untilAborted(signal, () =>\n\t\t\t\thandle.evaluate(el => (el as unknown as { tagName: string }).tagName),\n\t\t\t)) as string;\n\t\t\tif (tagName !== \"INPUT\")\n\t\t\t\tthrow new ToolError(\n\t\t\t\t\t`tab.uploadFile() requires an <input type=\"file\"> element (got <${tagName.toLowerCase()}>)`,\n\t\t\t\t);\n\t\t\tawait untilAborted(signal, () => upload.uploadFile(...absolute));\n\t\t} finally {\n\t\t\tawait handle.dispose().catch(() => undefined);\n\t\t}\n\t}\n\n\tasync #waitForUrl(pattern: string | RegExp, timeout: number, signal: AbortSignal): Promise<string> {\n\t\tconst page = this.#requirePage();\n\t\tconst isRegex = pattern instanceof RegExp;\n\t\tconst matcher = isRegex ? pattern.source : pattern;\n\t\tconst flags = isRegex ? pattern.flags : \"\";\n\t\tawait untilAborted(signal, () =>\n\t\t\tpage.waitForFunction(\n\t\t\t\t(m: string, isRe: boolean, fl: string) => {\n\t\t\t\t\tconst url = (globalThis as unknown as { location: { href: string } }).location.href;\n\t\t\t\t\treturn isRe ? new RegExp(m, fl).test(url) : url.includes(m);","sourceCodeStart":2059,"sourceCodeEnd":2095,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/browser/tab-worker.ts#L2059-L2095","documentation":"Element.uploadFile() only works on <input type=\"file\"> elements. The worker checks tagName inside the page and throws this ToolError naming the actual tag when the resolved element is not an INPUT. Note the check is tagName-only; a non-file input (e.g. type=\"text\") will pass this gate but fail later inside uploadFile.","triggerScenarios":"tab.uploadFile(\"#dropzone\", [...]) where #dropzone is a div drag-and-drop zone, a button that opens a native file picker, or any non-input element; also targeting the form rather than its file input.","commonSituations":"Custom upload widgets (styled divs/buttons wrapping a hidden <input type=file>); targeting the visible drop area instead of the underlying input; sites that use drag-drop only with no file input at all.","solutions":["Locate the real <input type=\"file\"> in the DOM (often hidden) and pass its selector.","Make the hidden input selectable if needed, or resolve it via the wrapper: e.g. selector \"form input[type=file]\".","For drag-only uploaders, dispatch drop events via page evaluate with a DataTransfer, or restructure the page test.","Check the tag with a quick evaluate before calling if unsure."],"exampleFix":"// before\nawait tab.uploadFile(\"#upload-button\", [\"./doc.pdf\"]); // it's a <button>\n// after\nawait tab.uploadFile(\"form input[type=file]\", [\"./doc.pdf\"]); // hidden input behind the button","handlingStrategy":"validation","validationCode":"const info = await tab.evaluate((sel) => {\n  const el = document.querySelector(sel);\n  return { tag: el?.tagName, type: (el as HTMLInputElement)?.type ?? null };\n}, selector);\nif (info.tag !== \"INPUT\" || info.type !== \"file\") {\n  throw new Error(`${selector} is <${info.tag} type=${info.type}>; need <input type=file>`);\n}","typeGuard":"function isFileInput(info: { tag?: string | null; type?: string | null }): boolean {\n  return info.tag === \"INPUT\" && info.type === \"file\";\n}","tryCatchPattern":"try {\n  await tab.uploadFile(selector, filePaths);\n} catch (err) {\n  if (err.message.includes(\"requires an <input type=\\\"file\\\">\")) {\n    return tab.uploadFile(\"input[type=file]\", filePaths); // locate the real input\n  }\n  throw err;\n}","preventionTips":["Target the underlying <input type=file>, not the styled button/dropzone","Search the DOM for input[type=file] (often hidden) before uploading","For drag-only uploaders, dispatch drop events instead","Check tagName/type via evaluate when unsure"],"tags":["browser","upload","dom","wrong-element-type"],"backgroundTag":"wrong-element-type-for-action","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}