{"record":{"id":"65c739d8db79670c","repo":"jackwener/OpenCLI","slug":"accesskeyid-secretaccesskey-sessiontoken","errorCode":null,"errorMessage":"抖音上传授权缺少 AccessKeyID/SecretAccessKey/SessionToken","messagePattern":"抖音上传授权缺少 AccessKeyID/SecretAccessKey/SessionToken","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/douyin/_shared/vod-upload.js","lineNumber":119,"sourceCode":"  }\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) {\n    throw new CommandExecutionError('抖音上传授权缺少 AccessKeyID/SecretAccessKey/SessionToken');\n  }\n  return {\n    access_key_id: auth.AccessKeyID,\n    secret_access_key: auth.SecretAccessKey,\n    session_token: auth.SessionToken,\n    user_id: extractUserIdFromSessionToken(auth.SessionToken),\n    expired_time: auth.ExpiredTime,\n    current_time: auth.CurrentTime,\n  };\n}\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',","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/douyin/_shared/vod-upload.js#L101-L137","documentation":"The parsed auth JSON exists but is missing at least one of AccessKeyID, SecretAccessKey, or SessionToken — the trio required to sign AWS4 requests against the VOD upload API. The library refuses to continue with incomplete STS credentials since every subsequent signed call would fail.","triggerScenarios":"auth parses to an object lacking one or more of AccessKeyID/SecretAccessKey/SessionToken — e.g. Douyin returned an error-shaped or partially-populated credential object, or the field names changed (camelCase vs PascalCase).","commonSituations":"Douyin API field-name drift (e.g. AccessKeyId vs AccessKeyID); account restricted from STS issuance so only some fields returned; upstream schema change after an app update; clock/region issues returning a degraded payload.","solutions":["Log the parsed auth object to see exactly which keys are present and how they are cased.","Accept alternate casings in code (AccessKeyId/AccessKeyID, SessionToken/sessionToken) if Douyin changed field names.","Re-login and retry — a restricted or expired session can yield partial credentials.","Confirm the account can still get STS tokens for the VOD space (no admin-imposed restriction).","Update the CLI if a Douyin API version change renamed the credential fields."],"exampleFix":"// before\nif (!auth.AccessKeyID || !auth.SecretAccessKey || !auth.SessionToken) {\n  throw new CommandExecutionError('抖音上传授权缺少 AccessKeyID/SecretAccessKey/SessionToken');\n}\n// after\nconst accessKey = auth.AccessKeyID ?? auth.AccessKeyId;\nconst secretKey = auth.SecretAccessKey ?? auth.SecretAccessKeyId;\nconst token = auth.SessionToken ?? auth.sessionToken;\nif (!accessKey || !secretKey || !token) {\n  throw new CommandExecutionError('抖音上传授权缺少 AccessKeyID/SecretAccessKey/SessionToken');\n}","handlingStrategy":"validation","validationCode":"function hasCompleteStsCredentials(auth) {\n  return Boolean(auth && (auth.AccessKeyID || auth.AccessKeyId)\n    && (auth.SecretAccessKey || auth.SecretAccessKeyId)\n    && (auth.SessionToken || auth.sessionToken));\n}\n// before signing requests:\nif (!hasCompleteStsCredentials(auth)) throw new Error('STS 凭证不完整');","typeGuard":"null","tryCatchPattern":"try {\n  const creds = await getUploadAuthV5Credentials(page);\n} catch (e) {\n  if (/缺少 AccessKeyID/.test(e.message)) {\n    console.error('收到的 auth 键:', Object.keys(lastAuthJson)); // inspect and adapt\n  }\n  throw e;\n}","preventionTips":["Log parsed auth keys on failure to spot casing/renaming drift early.","Refresh sessions before upload batches — restricted sessions yield partial credentials.","Verify account STS issuance rights periodically.","Add schema tests against a captured real response."],"tags":["douyin","credentials","schema-validation","missing-field"],"backgroundTag":"missing-credentials-fields","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}