jackwener/OpenCLI · error · CommandExecutionError
抖音上传授权缺少 AccessKeyID/SecretAccessKey/SessionToken
Error message
抖音上传授权缺少 AccessKeyID/SecretAccessKey/SessionToken
What it means
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.
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.
Example fix
// before
if (!auth.AccessKeyID || !auth.SecretAccessKey || !auth.SessionToken) {
throw new CommandExecutionError('抖音上传授权缺少 AccessKeyID/SecretAccessKey/SessionToken');
}
// after
const accessKey = auth.AccessKeyID ?? auth.AccessKeyId;
const secretKey = auth.SecretAccessKey ?? auth.SecretAccessKeyId;
const token = auth.SessionToken ?? auth.sessionToken;
if (!accessKey || !secretKey || !token) {
throw new CommandExecutionError('抖音上传授权缺少 AccessKeyID/SecretAccessKey/SessionToken');
} Defensive patterns
Strategy: validation
Validate before calling
function hasCompleteStsCredentials(auth) {
return Boolean(auth && (auth.AccessKeyID || auth.AccessKeyId)
&& (auth.SecretAccessKey || auth.SecretAccessKeyId)
&& (auth.SessionToken || auth.sessionToken));
}
// before signing requests:
if (!hasCompleteStsCredentials(auth)) throw new Error('STS 凭证不完整'); Type guard
null
Try / catch
try {
const creds = await getUploadAuthV5Credentials(page);
} catch (e) {
if (/缺少 AccessKeyID/.test(e.message)) {
console.error('收到的 auth 键:', Object.keys(lastAuthJson)); // inspect and adapt
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- 申请抖音上传地址响应缺少必要字段
- creator.douyin.com
- Flomo API returned a memo without slug/id
- did not include a stable text value.
- Sales Navigator lead row missing name
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/65c739d8db79670c.
Report an issue: GitHub.
Appendix: source
Thrown at clis/douyin/_shared/vod-upload.js:119
}
if (result.status_code !== 0) {
const message = result.status_msg ?? result.message ?? 'unknown error';
if (result.status_code === 401 || result.status_code === 403 || /login|cookie|auth|captcha|verify|forbidden|permission|登录|登陆|权限|验证|验证码/i.test(String(message))) {
throw new AuthRequiredError('creator.douyin.com', `获取抖音上传授权失败: ${message}`);
}
throw new CommandExecutionError(`获取抖音上传授权失败: ${JSON.stringify(result)}`);
}
if (!result.auth) {
throw new CommandExecutionError(`获取抖音上传授权失败: ${JSON.stringify(result)}`);
}
let auth;
try {
auth = JSON.parse(result.auth);
} catch (error) {
throw new CommandExecutionError(`解析抖音上传授权失败: ${error instanceof Error ? error.message : String(error)}`);
}
if (!auth.AccessKeyID || !auth.SecretAccessKey || !auth.SessionToken) {
throw new CommandExecutionError('抖音上传授权缺少 AccessKeyID/SecretAccessKey/SessionToken');
}
return {
access_key_id: auth.AccessKeyID,
secret_access_key: auth.SecretAccessKey,
session_token: auth.SessionToken,
user_id: extractUserIdFromSessionToken(auth.SessionToken),
expired_time: auth.ExpiredTime,
current_time: auth.CurrentTime,
};
}
export async function applyVideoUploadInner(fileSize, credentials) {
const params = new URLSearchParams({
Action: 'ApplyUploadInner',
Version: '2020-11-19',
SpaceName: VOD_SPACE_NAME,
FileType: 'video',
IsInner: '1',View on GitHub (pinned to 49907e53dc)