{"record":{"id":"7103d5e9889bc484","repo":"jackwener/OpenCLI","slug":"imagex-upload-failed-with-status-res-status","errorCode":null,"errorMessage":"ImageX upload failed with status ${res.status}: ${body}","messagePattern":"ImageX upload failed with status (.+?): (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/douyin/_shared/imagex-upload.js","lineNumber":50,"sourceCode":" * @returns The store_uri (= image_uri for use in create_v2)\n */\nexport async function imagexUpload(imagePath, uploadInfo) {\n    if (!fs.existsSync(imagePath)) {\n        throw new CommandExecutionError(`Cover image file not found: ${imagePath}`, 'Ensure the file path is correct and accessible.');\n    }\n    const imageBuffer = fs.readFileSync(imagePath);\n    const contentType = detectContentType(imagePath);\n    const res = await fetch(uploadInfo.upload_url, {\n        method: 'PUT',\n        headers: {\n            'Content-Type': contentType,\n            'Content-Length': String(imageBuffer.byteLength),\n        },\n        body: imageBuffer,\n    });\n    if (!res.ok) {\n        const body = await res.text().catch(() => '');\n        throw new CommandExecutionError(`ImageX upload failed with status ${res.status}: ${body}`, 'Check that the upload URL is valid and has not expired.');\n    }\n    return uploadInfo.store_uri;\n}\n","sourceCodeStart":32,"sourceCodeEnd":54,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/douyin/_shared/imagex-upload.js#L32-L54","documentation":"After PUTting the image buffer to the ImageX upload_url, imagexUpload checks res.ok and throws this CommandExecutionError with the HTTP status and response body if the upload was rejected. The body is included because ImageX error payloads usually state why (expired URL, bad content type, size limits).","triggerScenarios":"fetch PUT to uploadInfo.upload_url returns a non-2xx status: expired upload_url/token, wrong Content-Type, image too large, or ImageX service rejection.","commonSituations":"Delaying too long between the apply-cover-upload call and the PUT so the signed URL expires; uploading an unsupported format; network proxy intercepting the request; image exceeding size limits.","solutions":["Re-request the upload info (apply cover upload) to get a fresh upload_url and retry immediately.","Check the error body in the message for specifics (expired signature, invalid content type, size limit).","Confirm the image format/size meets ImageX requirements and convert/compress if needed.","Retry once on 5xx statuses; treat 4xx as a parameter/URL problem rather than a transient failure."],"exampleFix":"// before: reusing an old uploadInfo from a previous run\nawait imagexUpload(p, cachedUploadInfo);\n// after: fetch fresh upload info right before upload\nconst uploadInfo = await applyCoverUpload(page, videoId);\nawait imagexUpload(p, uploadInfo);","handlingStrategy":"retry","validationCode":"// request fresh upload info immediately before uploading\nconst uploadInfo = await applyCoverUpload(page); // upload_url is short-lived\nif (!uploadInfo?.upload_url) throw new Error('no upload_url — apply step failed');","typeGuard":"null","tryCatchPattern":"try {\n  return await imagexUpload(p, uploadInfo);\n} catch (e) {\n  if (/status 5\\d\\d/.test(e.message)) {\n    await sleep(5000);\n    return imagexUpload(p, uploadInfo); // transient — retry\n  }\n  if (/status 4\\d\\d/.test(e.message)) {\n    const fresh = await applyCoverUpload(page); // likely expired URL\n    return imagexUpload(p, fresh);\n  }\n  throw e;\n}","preventionTips":["Fetch upload_url immediately before uploading; it expires quickly","Keep cover images within ImageX size limits and supported formats","Don't cache/reuse uploadInfo across runs","Inspect the response body in the error message for the exact rejection reason"],"tags":["douyin","imagex","upload","http-status"],"backgroundTag":"upload-request-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}