{"record":{"id":"80213d13a4db33c7","repo":"jackwener/OpenCLI","slug":"json-http-res-status-text-slic","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":193,"sourceCode":"  const params = new URLSearchParams({\n    Action: 'CommitUploadInner',\n    Version: '2020-11-19',\n    SpaceName: VOD_SPACE_NAME,\n  });\n  const url = `${VOD_UPLOAD_HOST}?${params.toString()}`;\n  const body = JSON.stringify({ SessionKey: uploadInfo.session_key });\n  const headers = computeAws4Headers(url, credentials, {\n    method: 'POST',\n    body,\n    headers: { 'content-type': 'application/json;charset=UTF-8' },\n  });\n  const res = await fetch(url, { method: 'POST', headers, body, 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 result = payload?.Result?.Results?.[0] ?? payload?.Result ?? {};\n  const videoId = result.Vid ?? result.VideoId ?? result.VideoID ?? result.vid ?? uploadInfo.video_id;\n  if (!videoId) {\n    throw new CommandExecutionError(`提交抖音上传响应缺少 video id: ${JSON.stringify(payload).slice(0, 500)}`);\n  }\n  const meta = result.Meta ?? result.VideoMeta ?? {};\n  return {\n    video_id: videoId,\n    poster_uri: result.PosterUri ?? result.PosterURI ?? result.SnapshotUri ?? result.SnapshotURI ?? '',\n    width: Number(meta.Width ?? meta.width ?? 720) || 720,\n    height: Number(meta.Height ?? meta.height ?? 1280) || 1280,\n    raw: result,\n  };","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/douyin/_shared/vod-upload.js#L175-L211","documentation":"commitVideoUploadInner POSTs CommitUploadInner and expects a JSON body; this error fires when JSON.parse fails on the response text, including the HTTP status and the first 300 characters of the raw body. It indicates the commit endpoint returned HTML, an empty body, or another non-JSON payload instead of the expected API envelope.","triggerScenarios":"The CommitUploadInner fetch returns non-JSON: WAF/CAPTCHA HTML page, gateway 502/504 error page, empty body, or a redirect to login due to expired credentials.","commonSituations":"Expired STS token causing edge errors; risk-control interception of the commit request; transient CDN/edge failures; proxy/VPN middleboxes altering the response; Douyin endpoint changes.","solutions":["Inspect the body snippet in the message — HTML suggests login/WAF issues; re-authenticate or complete verification and retry.","Refresh STS credentials via getUploadAuthV5Credentials and re-run commit if the token expired.","Retry with backoff for 5xx statuses — gateway failures are often transient.","Bypass VPN/proxy to rule out response rewriting by middleboxes.","Note that the upload itself may still have succeeded even if commit failed non-JSON; check the video id on the platform before re-uploading."],"exampleFix":"// before\nlet payload;\ntry {\n  payload = JSON.parse(text);\n} catch {\n  throw new CommandExecutionError(`提交抖音上传失败，非 JSON 响应: HTTP ${res.status} ${text.slice(0, 300)}`);\n}\n// after\nlet payload;\ntry {\n  payload = JSON.parse(text);\n} catch {\n  if (res.status >= 500) return retryCommitWithBackoff(); // transient gateway failure\n  throw new CommandExecutionError(`提交抖音上传失败，非 JSON 响应: HTTP ${res.status} ${text.slice(0, 300)}`);\n}","handlingStrategy":"retry","validationCode":"// refresh credentials if expired before committing\nif (credentials.expired_time && credentials.expired_time * 1000 < Date.now()) {\n  credentials = await getUploadAuthV5Credentials(page);\n}","typeGuard":"null","tryCatchPattern":"try {\n  return await commitVideoUploadInner(uploadInfo, creds);\n} catch (e) {\n  if (/非 JSON 响应/.test(e.message)) {\n    if (/HTTP 5\\d\\d/.test(e.message)) { await sleep(3000); return retry(); }\n    if (/<html/i.test(e.message)) throw new AuthRequiredError('creator.douyin.com', e.message);\n  }\n  throw e;\n}","preventionTips":["Commit promptly after upload; expired tokens mid-flow cause edge errors.","Retry transient 5xx with backoff before giving up.","Check whether the video actually committed before re-uploading (commit may have succeeded server-side).","Avoid VPN/proxy paths that can inject HTML error pages."],"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"}