jackwener/OpenCLI · error · CommandExecutionError
申请抖音上传地址失败,非 JSON 响应: HTTP ${res.status} ${text.slice(0, 300
Error message
申请抖音上传地址失败,非 JSON 响应: HTTP ${res.status} ${text.slice(0, 300)} What it means
applyVideoUploadInner calls the VOD ApplyUploadInner endpoint and expects a JSON body; this error fires when JSON.parse fails on the HTTP response text. The HTTP status and the first 300 characters of the raw body are included so you can see what the server actually returned (HTML error page, empty body, gateway text, etc.).
Source
Thrown at clis/douyin/_shared/vod-upload.js:147
}
export async function applyVideoUploadInner(fileSize, credentials) {
const params = new URLSearchParams({
Action: 'ApplyUploadInner',
Version: '2020-11-19',
SpaceName: VOD_SPACE_NAME,
FileType: 'video',
IsInner: '1',
FileSize: String(fileSize),
});
const url = `${VOD_UPLOAD_HOST}?${params.toString()}`;
const res = await fetch(url, { headers: computeAws4Headers(url, credentials), signal: AbortSignal.timeout(30000) });
const text = await res.text();
let payload;
try {
payload = JSON.parse(text);
} catch {
throw new CommandExecutionError(`申请抖音上传地址失败,非 JSON 响应: HTTP ${res.status} ${text.slice(0, 300)}`);
}
const error = payload?.ResponseMetadata?.Error;
if (!res.ok || error) {
throw new CommandExecutionError(`申请抖音上传地址失败: HTTP ${res.status} ${JSON.stringify(error ?? payload)}`);
}
const uploadNode = payload?.Result?.InnerUploadAddress?.UploadNodes?.[0];
const storeInfo = uploadNode?.StoreInfos?.[0];
const videoId = payload?.Result?.Vid || uploadNode?.Vid;
const sessionKey = uploadNode?.SessionKey ?? storeInfo?.SessionKey ?? payload?.Result?.SessionKey;
if (!uploadNode?.UploadHost || !storeInfo?.StoreUri || !storeInfo?.Auth || !videoId || !sessionKey) {
throw new CommandExecutionError(`申请抖音上传地址响应缺少必要字段: ${JSON.stringify(payload).slice(0, 500)}`);
}
return {
video_id: videoId,
tos_upload_url: `https://${uploadNode.UploadHost}/${storeInfo.StoreUri}`,
auth: storeInfo.Auth,
session_key: sessionKey,
upload_header: uploadNode.UploadHeader ?? {},View on GitHub (pinned to 49907e53dc)
Solutions
- Inspect the body snippet in the message — HTML indicates a WAF/login/CAPTCHA page; re-login or complete verification and retry.
- Refresh the STS credentials via getUploadAuthV5Credentials and retry (expired tokens often trigger edge errors).
- Retry with backoff if the status is 5xx — could be a transient gateway/CDN issue.
- Disable VPN/proxy or switch networks to rule out middlebox interference.
- Verify the request URL/headers are current in case Douyin moved the endpoint.
Example fix
// before
const res = await fetch(url, { headers: computeAws4Headers(url, credentials) });
const text = await res.text();
payload = JSON.parse(text); // throws generic SyntaxError
// after
const res = await fetch(url, { headers: computeAws4Headers(url, credentials) });
const text = await res.text();
try {
payload = JSON.parse(text);
} catch {
if (/<html|<HTML/.test(text)) await refreshLoginAndRetry(); // HTML => re-auth path
throw new CommandExecutionError(`申请抖音上传地址失败,非 JSON 响应: HTTP ${res.status} ${text.slice(0, 300)}`);
} Defensive patterns
Strategy: retry
Validate before calling
// sanity-check credentials freshness before the call
if (credentials.expired_time && credentials.expired_time * 1000 < Date.now()) {
throw new Error('STS 凭证已过期,请先刷新');
} Type guard
null
Try / catch
try {
return await applyVideoUploadInner(fileSize, creds);
} catch (e) {
if (/非 JSON 响应/.test(e.message) && /HTTP 5\d\d/.test(e.message)) {
await sleep(3000);
return applyVideoUploadInner(fileSize, creds); // transient gateway failure
}
if (/<html/i.test(e.message)) throw new AuthRequiredError('creator.douyin.com', e.message);
throw e;
} Prevention
- Refresh STS credentials before long upload sessions.
- Avoid VPN/proxy environments prone to HTML interception pages.
- Retry 5xx responses with exponential backoff.
- Route HTML-looking failures into your re-login flow.
When it happens
Trigger: The ApplyUploadInner fetch returns a non-JSON body: an HTML/gateway error page (502/504 from CDN/WAF), an empty response, a CAPTCHA/risk-control HTML page, or a redirect to a login page.
Common situations: Expired STS credentials causing the edge to return an HTML error; WAF/risk-control intercepting the request; network middleboxes (corporate proxy, VPN) rewriting responses; Douyin edge outage returning non-JSON 5xx bodies.
Related errors
- 提交抖音上传失败,非 JSON 响应: HTTP ${res.status} ${text.slice(0, 300)}
- Boss API request failed: ${message}
- coingecko categories returned malformed JSON: ${err?.message
- Douyin API request failed (${method} ${url}): ${error instan
- 解析抖音上传授权失败: ${error instanceof Error ? error.message : Strin
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/2feb2fcc3bf76473.
Report an issue: GitHub.