{"record":{"id":"e890584b95877d75","repo":"can1357/oh-my-pi","slug":"tab-uploadfile-requires-an-input-type-file-ele","errorCode":null,"errorMessage":"tab.uploadFile() requires an <input type=file> element","messagePattern":"tab\\.uploadFile\\(\\) requires an <input type=file> element","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/browser/cmux/cmux-tab.ts","lineNumber":962,"sourceCode":"\t\t\t\t\tsetValue(element, String(args.value || \"\"), false);\n\t\t\t\t\treturn true;\n\t\t\t\tcase \"scrollIntoView\":\n\t\t\t\t\treturn true;\n\t\t\t\tcase \"select\": {\n\t\t\t\t\tconst values = Array.isArray(args.values) ? args.values.map(String) : [String(args.value || \"\")];\n\t\t\t\t\tif (element.tagName !== \"SELECT\") throw new Error(\"tab.select() requires a <select> element\");\n\t\t\t\t\tconst wanted = new Set(values);\n\t\t\t\t\tconst selected = [];\n\t\t\t\t\tfor (const option of Array.from(element.options)) {\n\t\t\t\t\t\toption.selected = wanted.has(option.value);\n\t\t\t\t\t\tif (option.selected) selected.push(option.value);\n\t\t\t\t\t}\n\t\t\t\t\tinputEvent(element);\n\t\t\t\t\treturn selected;\n\t\t\t\t}\n\t\t\t\tcase \"uploadFile\": {\n\t\t\t\t\tif (element.tagName !== \"INPUT\" || element.type !== \"file\") {\n\t\t\t\t\t\tthrow new Error(\"tab.uploadFile() requires an <input type=file> element\");\n\t\t\t\t\t}\n\t\t\t\t\tconst transfer = new DataTransfer();\n\t\t\t\t\tfor (const file of args.files || []) {\n\t\t\t\t\t\tconst bytes = Uint8Array.from(atob(file.data), char => char.charCodeAt(0));\n\t\t\t\t\t\ttransfer.items.add(new File([bytes], file.name, { type: file.type || \"application/octet-stream\" }));\n\t\t\t\t\t}\n\t\t\t\t\telement.files = transfer.files;\n\t\t\t\t\tinputEvent(element);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new Error(\"Unsupported selector action \" + action);\n\t\t})()`;\n\t\tconst result = (await this.#request(\"browser.eval\", { script }, this.#runContext?.timeoutMs)) as CmuxEvalResult;\n\t\treturn result.value as TResult;\n\t}\n\n\tasync #waitForSelector(selector: string, timeoutMs: number): Promise<void> {","sourceCodeStart":944,"sourceCodeEnd":980,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/browser/cmux/cmux-tab.ts#L944-L980","documentation":"The `uploadFile` selector action requires an `<input type=\"file\">` element: it checks `element.tagName !== \"INPUT\" || element.type !== \"file\"` before constructing a DataTransfer of File objects and assigning them to `element.files`. Throwing early avoids the silent no-op (or TypeError) of assigning files to a non-file input.","triggerScenarios":"Calling the uploadFile action against a selector that matches a text input, a styled button/div that wraps the real file input, or a ref captured before the file input was rendered.","commonSituations":"Upload widgets that hide the actual `<input type=file>` behind a styled label; clicking-to-open dialogs instead of driving the input; automation of drag-and-drop-only upload zones that never expose a file input at the targeted node.","solutions":["Target the actual `<input type=file>` (often hidden): use a selector like `input[type=file]` instead of the visible button.","If the input is display:none, verify the bridge still allows dispatching on it; otherwise use drag-and-drop or clipboard actions if supported.","Re-capture the element ref after the upload widget mounts; stale refs may point at wrappers."],"exampleFix":"// before\ntab.uploadFile('button.upload', files);\n// after\ntab.uploadFile('input[type=file]', files); // target the real input, not the styled button","handlingStrategy":"validation","validationCode":"const info = await tab.evaluate(`(() => { const el = document.querySelector(${JSON.stringify(sel)}); return el ? el.tagName + ':' + el.type : 'missing'; })()`);\nif (info !== 'INPUT:file') throw new Error(`uploadFile needs input[type=file], got ${info}`);","typeGuard":"function isFileInput(el): el is HTMLInputElement { return el instanceof HTMLInputElement && el.type === 'file'; }","tryCatchPattern":null,"preventionTips":["Always target `input[type=file]`, never the styled button/label that opens the picker.","Capture element refs after the upload widget has mounted.","Note hidden inputs still work when driven programmatically via DataTransfer."],"tags":["dom","file-upload","selector","element-type-mismatch"],"backgroundTag":"wrong-element-type-for-action","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}