{"record":{"id":"dc7dba9af106bcab","repo":"jackwener/OpenCLI","slug":"image-upload-failed-msg","errorCode":null,"errorMessage":"Image upload failed: ${msg}","messagePattern":"Image upload failed: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"clis/twitter/utils.js","lineNumber":139,"sourceCode":" * After upload it polls the DOM briefly to confirm the preview thumbnail\n * actually rendered — without this, a 200 from setFileInput could mask a\n * silent-no-attachment post.\n *\n * @param {object} page - OpenCLI page handle.\n * @param {string} absImagePath - Already-validated absolute path.\n * @param {string} [fileInputSelector] - Override (post.js historically used\n *   the same selector; default matches the X composer route).\n */\nexport async function attachComposerImage(page, absImagePath, fileInputSelector = COMPOSER_FILE_INPUT_SELECTOR) {\n    let uploaded = false;\n    if (page.setFileInput) {\n        try {\n            await page.setFileInput([absImagePath], fileInputSelector);\n            uploaded = true;\n        } catch (err) {\n            const msg = err instanceof Error ? err.message : String(err);\n            if (!isRecoverableFileInputError(msg)) {\n                throw new Error(`Image upload failed: ${msg}`);\n            }\n            // setFileInput not supported by extension — fall through to base64 fallback.\n        }\n    }\n    if (!uploaded) {\n        const ext = path.extname(absImagePath).toLowerCase();\n        const mimeType = ext === '.png'\n            ? 'image/png'\n            : ext === '.gif'\n                ? 'image/gif'\n                : ext === '.webp'\n                    ? 'image/webp'\n                    : 'image/jpeg';\n        const base64 = fs.readFileSync(absImagePath).toString('base64');\n        if (base64.length > 500_000) {\n            console.warn(`[warn] Image base64 payload is ${(base64.length / 1024 / 1024).toFixed(1)}MB. ` +\n                'This may fail with the browser bridge. Update the extension to v1.6+ for CDP-based upload, ' +\n                'or compress the image before attaching.');","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/utils.js#L121-L157","documentation":"Thrown by attachComposerImage when the CDP-based page.setFileInput bridge exists and is invoked, but fails with an error that is NOT a recoverable 'unknown action / not supported / not-allowed' condition (see isRecoverableFileInputError). Only non-recoverable failures get wrapped as `Image upload failed: ${msg}`; recoverable ones silently fall through to the base64 DataTransfer fallback.","triggerScenarios":"page.setFileInput([absImagePath], selector) throws for reasons outside the recoverable regex: the composer file input selector matches nothing in the CDP bridge, the page/extension bridge errors mid-injection, a protocol/timeout error from the browser, or a permissions failure not phrased as NotAllowedError.","commonSituations":"Stale DOM after X re-renders the composer so the selector no longer resolves; outdated or missing OpenCLI browser extension producing unexpected bridge errors; browser crashed or tab navigated away mid-upload; selector override argument pointing at the wrong page's input.","solutions":["Re-run the command after reloading the composer page so the file input exists again","Check that the file input selector (default COMPOSER_FILE_INPUT_SELECTOR or your override) matches the current X compose DOM","Update the OpenCLI browser extension to the latest version so setFileInput behaves as expected","If the wrapped message indicates a browser/CDP failure, restart the browser session and retry; as a last resort compress the image so the base64 fallback path is viable"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if (!fs.existsSync(absImagePath) || !path.isAbsolute(absImagePath)) throw new Error('Provide an absolute path to an existing image before calling attachComposerImage');","typeGuard":"function isRecoverableUploadError(err) {\n  const msg = err instanceof Error ? err.message : String(err);\n  return /unknown action|not supported|not[-\\s]?allowed|notallowederror/i.test(msg);\n}","tryCatchPattern":"try {\n  await attachComposerImage(page, absPath);\n} catch (err) {\n  if (err.message.startsWith('Image upload failed:') && !isRecoverableFileInputError(err.message)) {\n    console.error('CDP setFileInput failed:', err.message);\n    // reload the composer page or update the extension, then retry\n  } else { throw err; }\n}","preventionTips":["Keep the OpenCLI browser extension up to date so setFileInput is available and stable","Reload the compose page before attaching so the file input selector resolves","Avoid overriding fileInputSelector unless you have verified the current DOM","Retry once after a page reload — many bridge failures are transient DOM timing issues"],"tags":["image-upload","browser-automation","cdp","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"}