jackwener/OpenCLI · error · CommandExecutionError
ImageX upload failed with status ${res.status}: ${body}
Error message
ImageX upload failed with status ${res.status}: ${body} What it means
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).
Source
Thrown at clis/douyin/_shared/imagex-upload.js:50
* @returns The store_uri (= image_uri for use in create_v2)
*/
export async function imagexUpload(imagePath, uploadInfo) {
if (!fs.existsSync(imagePath)) {
throw new CommandExecutionError(`Cover image file not found: ${imagePath}`, 'Ensure the file path is correct and accessible.');
}
const imageBuffer = fs.readFileSync(imagePath);
const contentType = detectContentType(imagePath);
const res = await fetch(uploadInfo.upload_url, {
method: 'PUT',
headers: {
'Content-Type': contentType,
'Content-Length': String(imageBuffer.byteLength),
},
body: imageBuffer,
});
if (!res.ok) {
const body = await res.text().catch(() => '');
throw new CommandExecutionError(`ImageX upload failed with status ${res.status}: ${body}`, 'Check that the upload URL is valid and has not expired.');
}
return uploadInfo.store_uri;
}
View on GitHub (pinned to 49907e53dc)
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.
Example fix
// before: reusing an old uploadInfo from a previous run await imagexUpload(p, cachedUploadInfo); // after: fetch fresh upload info right before upload const uploadInfo = await applyCoverUpload(page, videoId); await imagexUpload(p, uploadInfo);
Defensive patterns
Strategy: retry
Validate before calling
// request fresh upload info immediately before uploading
const uploadInfo = await applyCoverUpload(page); // upload_url is short-lived
if (!uploadInfo?.upload_url) throw new Error('no upload_url — apply step failed'); Type guard
null
Try / catch
try {
return await imagexUpload(p, uploadInfo);
} catch (e) {
if (/status 5\d\d/.test(e.message)) {
await sleep(5000);
return imagexUpload(p, uploadInfo); // transient — retry
}
if (/status 4\d\d/.test(e.message)) {
const fresh = await applyCoverUpload(page); // likely expired URL
return imagexUpload(p, fresh);
}
throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- ${action}失败: ${JSON.stringify(error)}
- Cover image file not found: ${imagePath}
- TOS init multipart upload failed with status ${res.status}:
- TOS init response missing UploadId: ${res.body}
- TOS upload part ${partNumber} failed with status ${res.statu
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/7103d5e9889bc484.
Report an issue: GitHub.