{"record":{"id":"2feb2fcc3bf76473","repo":"jackwener/OpenCLI","slug":"json-http-res-status-text-sl","errorCode":null,"errorMessage":"申请抖音上传地址失败，非 JSON 响应: HTTP ${res.status} ${text.slice(0, 300)}","messagePattern":"申请抖音上传地址失败，非 JSON 响应: HTTP (.+?) (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/douyin/_shared/vod-upload.js","lineNumber":147,"sourceCode":"}\n\nexport async function applyVideoUploadInner(fileSize, credentials) {\n  const params = new URLSearchParams({\n    Action: 'ApplyUploadInner',\n    Version: '2020-11-19',\n    SpaceName: VOD_SPACE_NAME,\n    FileType: 'video',\n    IsInner: '1',\n    FileSize: String(fileSize),\n  });\n  const url = `${VOD_UPLOAD_HOST}?${params.toString()}`;\n  const res = await fetch(url, { headers: computeAws4Headers(url, credentials), signal: AbortSignal.timeout(30000) });\n  const text = await res.text();\n  let payload;\n  try {\n    payload = JSON.parse(text);\n  } catch {\n    throw new CommandExecutionError(`申请抖音上传地址失败，非 JSON 响应: HTTP ${res.status} ${text.slice(0, 300)}`);\n  }\n  const error = payload?.ResponseMetadata?.Error;\n  if (!res.ok || error) {\n    throw new CommandExecutionError(`申请抖音上传地址失败: HTTP ${res.status} ${JSON.stringify(error ?? payload)}`);\n  }\n  const uploadNode = payload?.Result?.InnerUploadAddress?.UploadNodes?.[0];\n  const storeInfo = uploadNode?.StoreInfos?.[0];\n  const videoId = payload?.Result?.Vid || uploadNode?.Vid;\n  const sessionKey = uploadNode?.SessionKey ?? storeInfo?.SessionKey ?? payload?.Result?.SessionKey;\n  if (!uploadNode?.UploadHost || !storeInfo?.StoreUri || !storeInfo?.Auth || !videoId || !sessionKey) {\n    throw new CommandExecutionError(`申请抖音上传地址响应缺少必要字段: ${JSON.stringify(payload).slice(0, 500)}`);\n  }\n  return {\n    video_id: videoId,\n    tos_upload_url: `https://${uploadNode.UploadHost}/${storeInfo.StoreUri}`,\n    auth: storeInfo.Auth,\n    session_key: sessionKey,\n    upload_header: uploadNode.UploadHeader ?? {},","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/douyin/_shared/vod-upload.js#L129-L165","documentation":"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.).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst res = await fetch(url, { headers: computeAws4Headers(url, credentials) });\nconst text = await res.text();\npayload = JSON.parse(text); // throws generic SyntaxError\n// after\nconst res = await fetch(url, { headers: computeAws4Headers(url, credentials) });\nconst text = await res.text();\ntry {\n  payload = JSON.parse(text);\n} catch {\n  if (/<html|<HTML/.test(text)) await refreshLoginAndRetry(); // HTML => re-auth path\n  throw new CommandExecutionError(`申请抖音上传地址失败，非 JSON 响应: HTTP ${res.status} ${text.slice(0, 300)}`);\n}","handlingStrategy":"retry","validationCode":"// sanity-check credentials freshness before the call\nif (credentials.expired_time && credentials.expired_time * 1000 < Date.now()) {\n  throw new Error('STS 凭证已过期，请先刷新');\n}","typeGuard":"null","tryCatchPattern":"try {\n  return await applyVideoUploadInner(fileSize, creds);\n} catch (e) {\n  if (/非 JSON 响应/.test(e.message) && /HTTP 5\\d\\d/.test(e.message)) {\n    await sleep(3000);\n    return applyVideoUploadInner(fileSize, creds); // transient gateway failure\n  }\n  if (/<html/i.test(e.message)) throw new AuthRequiredError('creator.douyin.com', e.message);\n  throw e;\n}","preventionTips":["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."],"tags":["network","douyin","json-parse","non-json-response"],"backgroundTag":"non-json-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}