{"record":{"id":"80abf672a6565666","repo":"jackwener/OpenCLI","slug":"image-upload-failed-upload-error-unknown-e","errorCode":null,"errorMessage":"Image upload failed: ${upload?.error ?? 'unknown error'}","messagePattern":"Image upload failed: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"clis/twitter/utils.js","lineNumber":193,"sourceCode":"          assigned = input.files && input.files.length > 0;\n        } catch(e) {\n          // files property not redefinable — use nativeInputValueSetter trick\n          try {\n            const nativeInputFileSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'files');\n            if (nativeInputFileSetter && nativeInputFileSetter.set) {\n              nativeInputFileSetter.set.call(input, dt.files);\n              assigned = input.files && input.files.length > 0;\n            }\n          } catch(e2) { /* ignore */ }\n        }\n        if (!assigned) return { ok: false, error: 'Could not assign files to input' };\n        input.dispatchEvent(new Event('change', { bubbles: true }));\n        input.dispatchEvent(new Event('input', { bubbles: true }));\n        return { ok: true };\n      })()\n    `);\n        if (!upload?.ok) {\n            throw new Error(`Image upload failed: ${upload?.error ?? 'unknown error'}`);\n        }\n    }\n    const uploadState = await waitForComposerMediaReady(page, 1);\n    if (!uploadState?.ok) {\n        throw new Error(uploadState?.message ?? 'Image upload failed: preview did not appear.');\n    }\n}\n\nexport function isRecoverableFileInputError(error) {\n    const msg = error instanceof Error ? error.message : String(error);\n    return /unknown action|not supported|not[-\\s]?allowed|notallowederror/i.test(msg);\n}\n\nexport async function waitForComposerMediaReady(page, expectedCount = 1) {\n    const iterations = Math.ceil(MEDIA_UPLOAD_TIMEOUT_MS / MEDIA_UPLOAD_POLL_MS);\n    return page.evaluate(`\n    (async () => {\n      const expected = ${JSON.stringify(expectedCount)};","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/utils.js#L175-L211","documentation":"Thrown by attachComposerImage after the base64 DataTransfer fallback path runs inside page.evaluate. When the in-page IIFE returns { ok: false, error } — or nothing at all — the error is surfaced as `Image upload failed: ${upload?.error ?? 'unknown error'}`. Known in-page errors are 'No file input found on page' and 'Could not assign files to input'.","triggerScenarios":"The fallback path executes because setFileInput was missing or recoverably failed, and then either document.querySelector(fileInputSelector) finds no input, or the File/DataTransfer assignment fails because the input's `files` property cannot be redefined and the nativeInputValueSetter trick also fails.","commonSituations":"Hardened pages or CSP/DOM setups where HTMLInputElement.prototype.files cannot be patched and Object.defineProperty on the input is blocked; X changing the composer markup so the default selector no longer matches; running without the CDP-capable extension so only the fragile fallback path is used; very large images making the evaluate payload unreliable.","solutions":["Update the OpenCLI browser extension to v1.6+ so the reliable CDP setFileInput path is used instead of the base64 fallback","Verify the composer page is fully loaded and the file input selector still matches the current X DOM; update fileInputSelector if it changed","Compress the image below ~500KB base64 (the code warns above that size) and retry the fallback","Retry once after reloading the page — transient hydration/RAF timing can make the input briefly unassignable"],"exampleFix":"// before\nawait attachComposerImage(page, '/tmp/big.png'); // fallback path, 'Could not assign files to input'\n// after\n// compress first so base64 is small and update the extension\nexecSync('convert /tmp/big.png -resize 1600x /tmp/small.jpg');\nawait attachComposerImage(page, '/tmp/small.jpg');","handlingStrategy":"fallback","validationCode":"const hasInput = await page.evaluate(`!!document.querySelector(${JSON.stringify(COMPOSER_FILE_INPUT_SELECTOR)})`);\nif (!hasInput) throw new Error('Composer file input not present — open/reload the compose page first');","typeGuard":"function uploadResultOk(upload) {\n  return Boolean(upload && typeof upload === 'object' && upload.ok === true);\n}","tryCatchPattern":"try {\n  await attachComposerImage(page, absPath);\n} catch (err) {\n  if (err.message === 'Image upload failed: No file input found on page') {\n    await page.reload(); // then retry\n  } else if (err.message === 'Image upload failed: Could not assign files to input') {\n    console.error('Base64 fallback blocked; install/update the browser extension (v1.6+) for CDP upload');\n  } else { throw err; }\n}","preventionTips":["Use extension v1.6+ so the CDP path runs instead of the fragile base64 fallback","Verify the composer input selector against the live DOM before automating","Keep base64 payload small (<~500KB) — compress images before the fallback path","Test the fallback path after any X UI redesign"],"tags":["image-upload","browser-automation","dom","twitter-cli"],"backgroundTag":"dom-element-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}