{"record":{"id":"83acc4a331d78ae5","repo":"jackwener/OpenCLI","slug":"http-res-status-json-stringify-erro","errorCode":null,"errorMessage":"提交抖音上传失败: HTTP ${res.status} ${JSON.stringify(error ?? payload)}","messagePattern":"提交抖音上传失败: HTTP (.+?) (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/douyin/_shared/vod-upload.js","lineNumber":197,"sourceCode":"  });\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  };\n}\n","sourceCodeStart":179,"sourceCodeEnd":213,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/douyin/_shared/vod-upload.js#L179-L213","documentation":"Thrown by commitVideoUploadInner after calling the Douyin VOD CommitUploadInner API when the HTTP response is not OK or the JSON payload contains ResponseMetadata.Error. It wraps the full API error object so the developer can see why the commit failed (signature, session, or space problems). The library throws it because a non-ok commit means the uploaded video will never become a playable Vid.","triggerScenarios":"Calling commitVideoUploadInner with an expired/invalid SessionKey; wrong AWS4 credentials (access key/secret) so the VOD API rejects signature; VOD_SPACE_NAME not existing or unauthorized; the chunked upload never completed so CommitUploadInner rejects the session; transient VOD 5xx responses.","commonSituations":"Expired Volcano/ByteDance VOD credentials in env config; uploading a file then committing after session timeout; misconfigured space name after a Douyin backend change; clock skew invalidating AWS4 signature.","solutions":["Inspect the embedded ResponseMetadata.Error.Code in the message and match it against VodSpace/InvalidParameter errors","Re-run the upload from scratch to get a fresh SessionKey and commit promptly","Verify VOD access key/secret and VOD_SPACE_NAME env/config values","Check system clock skew if signature-related errors appear","Retry after a short delay if the status is 5xx/rate-limit"],"exampleFix":"// before: commit long after upload finished, session expired\nawait uploadChunks(...); await sleep(600000); await commitVideoUploadInner(info, creds);\n// after: commit immediately after upload\nawait uploadChunks(...);\nconst result = await commitVideoUploadInner(info, creds);","handlingStrategy":"try-catch","validationCode":"if (!process.env.VOD_ACCESS_KEY || !process.env.VOD_SECRET_KEY) throw new Error('VOD credentials missing');\nif (!uploadInfo?.session_key) throw new Error('session_key missing; re-run upload');","typeGuard":"function isVodError(payload) {\n  return Boolean(payload?.ResponseMetadata?.Error);\n}","tryCatchPattern":"try {\n  const committed = await commitVideoUploadInner(info, creds);\n} catch (e) {\n  if (/提交抖音上传失败/.test(e.message)) {\n    console.error('commit rejected:', e.message);\n    // re-upload + retry once with fresh session\n  } else throw e;\n}","preventionTips":["Commit immediately after the last chunk uploads to avoid SessionKey expiry","Validate VOD access key/secret/space config at startup","Log ResponseMetadata.Error.Code to build a retry table for known codes","Keep system clock in sync for AWS4 signatures"],"tags":["douyin","vod","upload","api-error"],"backgroundTag":"vod-commit-upload-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}