jackwener/OpenCLI · error · CommandExecutionError

解析抖音上传授权失败: ${error instanceof Error ? error.message : Strin

Error message

解析抖音上传授权失败: ${error instanceof Error ? error.message : String(error)}

What it means

getUploadAuthV5Credentials JSON-parses the result.auth string into a credentials object; this error wraps the JSON.parse failure. It means auth existed but was not valid JSON — truncated content, HTML injected into the response, or an encoded format change. The parse error message is included verbatim.

Source

Thrown at clis/douyin/_shared/vod-upload.js:116

  const result = unwrapEvaluateResult(await page.evaluate(`fetch(${JSON.stringify(AUTH_V5_URL)}, { credentials: 'include' }).then(r => r.json())`));
  if (!result || Array.isArray(result) || typeof result !== 'object') {
    throw new CommandExecutionError(`获取抖音上传授权失败: ${JSON.stringify(result)}`);
  }
  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',

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Log the raw auth string (first bytes) to see what was actually returned — HTML signals a login/redirect problem, so re-login.
  2. Re-login to creator.douyin.com and retry if the payload looks like a login page.
  3. If the content is base64/URL-encoded, decode before JSON.parse and update the code accordingly.
  4. Check for proxies, VPNs, or extensions altering page network responses.
  5. Retry on a fresh page load to rule out a corrupted intermediate response.

Example fix

// before
auth = JSON.parse(result.auth);
// after
let raw = result.auth;
if (/^STS?[0-9]/.test(raw) || /^[A-Za-z0-9+/=]+$/.test(raw)) {
  raw = Buffer.from(raw, 'base64').toString('utf8'); // decode before parse if encoded
}
auth = JSON.parse(raw);
Defensive patterns

Strategy: validation

Validate before calling

function looksLikeJsonAuth(s) {
  return typeof s === 'string' && s.trim().startsWith('{');
}
// before relying on result.auth:
if (!looksLikeJsonAuth(result.auth)) throw new Error('auth 字段不是 JSON 字符串');

Type guard

null

Try / catch

try {
  auth = JSON.parse(result.auth);
} catch (error) {
  console.error('原始 auth 内容:', String(result.auth).slice(0, 200));
  throw error; // HTML content => re-login path; encoded content => decode path
}

Prevention

When it happens

Trigger: result.auth contains non-JSON text: an HTML error/login page snippet, URL-encoded or base64 content, or a truncated string from an interrupted response.

Common situations: A captive portal or proxy replaced the response with HTML; Douyin changed the auth encoding (e.g. now base64 instead of JSON string); cookie-consent/login redirect HTML captured by the in-page fetch.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/462e130ddb64d694. Report an issue: GitHub.