jackwener/OpenCLI · error · CommandExecutionError
抖音封面申请上传地址响应缺少 UploadHost/StoreUri: ${JSON.stringify(applyRe
Error message
抖音封面申请上传地址响应缺少 UploadHost/StoreUri: ${JSON.stringify(applyRes).slice(0, 500)} What it means
The cover upload flow calls Douyin's ImageX ApplyImageUpload endpoint inside the page to get an upload host and store URI. If the response's Result.UploadAddress.StoreInfos[0] lacks UploadHost or StoreUri, publish.js throws this CommandExecutionError including the first 500 chars of the raw response for debugging. It indicates the ImageX service returned an unexpected/malformed payload rather than usable upload credentials.
Source
Thrown at clis/douyin/publish.js:176
const committedVideo = await commitVideoUploadInner(tosUploadInfo, credentials);
const videoId = committedVideo.video_id;
process.stderr.write(` 上传已提交: ${videoId}\n`);
coverWidth = committedVideo.width || coverWidth;
coverHeight = committedVideo.height || coverHeight;
if (!coverUri && committedVideo.poster_uri) {
coverUri = committedVideo.poster_uri;
}
// ── Phase 4: Cover upload (optional) ────────────────────────────────
if (kwargs.cover) {
const resolvedCoverPath = path.resolve(kwargs.cover);
// 4A: Apply ImageX upload
const applyUrl = `${IMAGEX_BASE}/?Action=ApplyImageUpload&ServiceId=${IMAGEX_SERVICE_ID}&Version=2018-08-01&UploadNum=1`;
const applyJs = `fetch(${JSON.stringify(applyUrl)}, { credentials: 'include' }).then(r => r.json())`;
const applyRes = requireObjectEvaluateResult(await page.evaluate(applyJs), '抖音封面申请上传地址响应异常');
throwIfImagexError('抖音封面申请上传地址', applyRes);
const imgStoreInfo = applyRes.Result?.UploadAddress?.StoreInfos?.[0];
if (!imgStoreInfo?.UploadHost || !imgStoreInfo?.StoreUri) {
throw new CommandExecutionError(`抖音封面申请上传地址响应缺少 UploadHost/StoreUri: ${JSON.stringify(applyRes).slice(0, 500)}`);
}
const imgUploadUrl = `https://${imgStoreInfo.UploadHost}/${imgStoreInfo.StoreUri}`;
// 4B: Upload image
const coverStoreUri = await imagexUpload(resolvedCoverPath, {
upload_url: imgUploadUrl,
store_uri: imgStoreInfo.StoreUri,
});
// 4C: Commit ImageX upload
const commitUrl = `${IMAGEX_BASE}/?Action=CommitImageUpload&ServiceId=${IMAGEX_SERVICE_ID}&Version=2018-08-01`;
const commitBody = JSON.stringify({ SuccessObjKeys: [coverStoreUri] });
const commitJs = `
fetch(${JSON.stringify(commitUrl)}, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: ${JSON.stringify(commitBody)}
}).then(r => r.json())
`;View on GitHub (pinned to 49907e53dc)
Solutions
- Inspect the truncated response JSON in the message to see the actual payload shape
- Re-login to Douyin in the automation browser to refresh the session/cookies used for the ImageX call
- Retry later if it's a transient ImageX outage; compare against a manual browser upload
- If the API shape changed, update the parsing path (Result.UploadAddress.StoreInfos[0]) in publish.js
Example fix
// before (fragile assumption)
const imgStoreInfo = applyRes.Result?.UploadAddress?.StoreInfos?.[0];
// after (explicit diagnosis)
const imgStoreInfo = applyRes.Result?.UploadAddress?.StoreInfos?.[0];
if (!imgStoreInfo) {
console.error('ImageX response:', JSON.stringify(applyRes));
throw new Error('Refresh Douyin login and retry ApplyImageUpload');
} Defensive patterns
Strategy: retry
Validate before calling
// No caller-side pre-check possible; validate session health instead
const loggedIn = await isLoggedIn(page); // probe a Douyin endpoint
if (!loggedIn) throw new Error('Refresh Douyin session before publishing'); Type guard
function hasUploadAddress(res) {
const info = res?.Result?.UploadAddress?.StoreInfos?.[0];
return typeof info?.UploadHost === 'string' && typeof info?.StoreUri === 'string' && info.UploadHost.length > 0;
} Try / catch
try {
await douyin.publish({ cover });
} catch (e) {
if (e instanceof CommandExecutionError && /UploadHost\/StoreUri/.test(e.message)) {
console.error('ImageX payload:', e.message); // inspect truncated JSON
// refresh login, then retry once
} else throw e;
} Prevention
- Keep the automation browser's Douyin session fresh
- Retry transient ImageX failures with backoff
- Log full ImageX responses when debugging payload-shape drift
- Track Douyin/ImageX API changelog for field renames
When it happens
Trigger: page.evaluate of the ApplyImageUpload fetch returns JSON whose Result.UploadAddress.StoreInfos is empty or its first entry is missing UploadHost/StoreUri — typically after throwIfImagexError found no explicit error code in the response.
Common situations: Expired or insufficient Douyin login session causing ImageX to return an empty/soft-error payload; ImageX API contract changed (new field layout or ServiceId no longer valid); regional/service outage returning partial JSON; response shaped as an error object not recognized by throwIfImagexError.
Related errors
- Bilibili ${label} API returned a malformed payload
- Bilibili ${label} API failed: ${message} (${payload.code})
- coingecko global returned malformed JSON: ${err?.message ??
- ImageX upload failed with status ${res.status}: ${body}
- ${action}失败: ${JSON.stringify(error)}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/600b07850886acc5.
Report an issue: GitHub.