{"record":{"id":"56516b641c388a67","repo":"jackwener/OpenCLI","slug":"json-stringify-result","errorCode":null,"errorMessage":"获取抖音上传授权失败: ${JSON.stringify(result)}","messagePattern":"获取抖音上传授权失败: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"critical","filePath":"clis/douyin/_shared/vod-upload.js","lineNumber":100,"sourceCode":"  try {\n    const raw = sessionToken.startsWith('STS2') ? sessionToken.slice(4) : sessionToken;\n    const decoded = JSON.parse(Buffer.from(raw, 'base64').toString('utf8'));\n    const policy = JSON.parse(decoded.PolicyString || '{}');\n    const condition = policy?.Statement?.[0]?.Condition;\n    if (typeof condition === 'string') {\n      const parsedCondition = JSON.parse(condition);\n      return parsedCondition.UserId || '';\n    }\n  } catch {\n    return '';\n  }\n  return '';\n}\n\nexport async function getUploadAuthV5Credentials(page) {\n  const result = unwrapEvaluateResult(await page.evaluate(`fetch(${JSON.stringify(AUTH_V5_URL)}, { credentials: 'include' }).then(r => r.json())`));\n  if (!result || Array.isArray(result) || typeof result !== 'object') {\n    throw new CommandExecutionError(`获取抖音上传授权失败: ${JSON.stringify(result)}`);\n  }\n  if (result.status_code !== 0) {\n    const message = result.status_msg ?? result.message ?? 'unknown error';\n    if (result.status_code === 401 || result.status_code === 403 || /login|cookie|auth|captcha|verify|forbidden|permission|登录|登陆|权限|验证|验证码/i.test(String(message))) {\n      throw new AuthRequiredError('creator.douyin.com', `获取抖音上传授权失败: ${message}`);\n    }\n    throw new CommandExecutionError(`获取抖音上传授权失败: ${JSON.stringify(result)}`);\n  }\n  if (!result.auth) {\n    throw new CommandExecutionError(`获取抖音上传授权失败: ${JSON.stringify(result)}`);\n  }\n  let auth;\n  try {\n    auth = JSON.parse(result.auth);\n  } catch (error) {\n    throw new CommandExecutionError(`解析抖音上传授权失败: ${error instanceof Error ? error.message : String(error)}`);\n  }\n  if (!auth.AccessKeyID || !auth.SecretAccessKey || !auth.SessionToken) {","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/douyin/_shared/vod-upload.js#L82-L118","documentation":"getUploadAuthV5Credentials fetches Douyin's v5 upload auth endpoint in the page context and unwraps the JSON; this CommandExecutionError is thrown when the result is not a plain object (null, array, or non-object), meaning the fetch/evaluate did not return usable credentials JSON.","triggerScenarios":"page.evaluate fetch returns null/undefined (network failure, JSON parse fail inside page), an array, or a non-object — e.g. the endpoint returned an HTML login page instead of JSON.","commonSituations":"Logged-out or expired session so the endpoint redirects; wrong page (not creator.douyin.com); evaluate sandbox blocked by CSP; endpoint schema change.","solutions":["Ensure the page is logged in to creator.douyin.com and reload before fetching","Navigate to creator.douyin.com explicitly so cookies/origin match AUTH_V5_URL","Log JSON.stringify(result) from the error to inspect what was actually returned","Check whether unwrapEvaluateResult is dropping the payload (evaluate serialization issues)"],"exampleFix":"// before\nconst creds = await getUploadAuthV5Credentials(anyPage);\n// after\nif (!page.url().includes('creator.douyin.com')) {\n  await page.goto('https://creator.douyin.com');\n}\nconst creds = await getUploadAuthV5Credentials(page);","handlingStrategy":"type-guard","validationCode":"if (!page.url().includes('creator.douyin.com')) {\n  await page.goto('https://creator.douyin.com', { waitUntil: 'domcontentloaded' });\n}","typeGuard":"function isUploadAuthResult(r) {\n  return !!r && typeof r === 'object' && !Array.isArray(r) && 'status_code' in r;\n}","tryCatchPattern":"try { const creds = await getUploadAuthV5Credentials(page); }\ncatch (e) {\n  if (String(e.message).startsWith('获取抖音上传授权失败')) {\n    await page.goto('https://creator.douyin.com');\n    const creds = await getUploadAuthV5Credentials(page);\n  } else throw e;\n}","preventionTips":["Always call this on a logged-in creator.douyin.com page","Navigate to the correct origin before evaluating fetches","Inspect the serialized result in the error message to spot HTML/login redirects","Add a shape check (isUploadAuthResult) before consuming the result"],"tags":["auth","douyin","fetch","upload"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}