jackwener/OpenCLI · error · CommandExecutionError

申请抖音上传地址失败: HTTP ${res.status} ${JSON.stringify(error ?? pay

Error message

申请抖音上传地址失败: HTTP ${res.status} ${JSON.stringify(error ?? payload)}

What it means

applyVideoUploadInner throws this when the ApplyUploadInner endpoint returned a parseable JSON response, but the request failed at the API level: either the HTTP status is not 2xx or ResponseMetadata.Error is present. The full error object (or payload) is JSON-stringified into the message, so the server's Code/Message are visible.

Source

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

    Action: 'ApplyUploadInner',
    Version: '2020-11-19',
    SpaceName: VOD_SPACE_NAME,
    FileType: 'video',
    IsInner: '1',
    FileSize: String(fileSize),
  });
  const url = `${VOD_UPLOAD_HOST}?${params.toString()}`;
  const res = await fetch(url, { headers: computeAws4Headers(url, credentials), signal: AbortSignal.timeout(30000) });
  const text = await res.text();
  let payload;
  try {
    payload = JSON.parse(text);
  } catch {
    throw new CommandExecutionError(`申请抖音上传地址失败,非 JSON 响应: HTTP ${res.status} ${text.slice(0, 300)}`);
  }
  const error = payload?.ResponseMetadata?.Error;
  if (!res.ok || error) {
    throw new CommandExecutionError(`申请抖音上传地址失败: HTTP ${res.status} ${JSON.stringify(error ?? payload)}`);
  }
  const uploadNode = payload?.Result?.InnerUploadAddress?.UploadNodes?.[0];
  const storeInfo = uploadNode?.StoreInfos?.[0];
  const videoId = payload?.Result?.Vid || uploadNode?.Vid;
  const sessionKey = uploadNode?.SessionKey ?? storeInfo?.SessionKey ?? payload?.Result?.SessionKey;
  if (!uploadNode?.UploadHost || !storeInfo?.StoreUri || !storeInfo?.Auth || !videoId || !sessionKey) {
    throw new CommandExecutionError(`申请抖音上传地址响应缺少必要字段: ${JSON.stringify(payload).slice(0, 500)}`);
  }
  return {
    video_id: videoId,
    tos_upload_url: `https://${uploadNode.UploadHost}/${storeInfo.StoreUri}`,
    auth: storeInfo.Auth,
    session_key: sessionKey,
    upload_header: uploadNode.UploadHeader ?? {},
    user_id: credentials.user_id ?? '',
  };
}

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Read ResponseMetadata.Error.Code/Message in the error message — SignatureDoesNotMatch/ExpiredToken means refresh STS credentials and retry.
  2. Sync the system clock (NTP) if signature/timestamp errors appear.
  3. Refresh credentials via getUploadAuthV5Credentials before retrying when the token expired.
  4. Validate the FileSize argument passed in (must be a positive integer matching the real file).
  5. Retry with backoff for 5xx/throttling codes; reduce request rate if throttled.

Example fix

// before
await applyVideoUploadInner(fileSize, staleCredentials);
// after
if (isExpired(credentials, now)) {
  credentials = await getUploadAuthV5Credentials(page); // refresh STS before applying
}
await applyVideoUploadInner(fileSize, credentials);
Defensive patterns

Strategy: retry

Validate before calling

// check clock skew and token expiry before signing
if (Math.abs(Date.now() - await ntpNow()) > 5 * 60 * 1000) syncClock();
if (credentials.expired_time * 1000 <= Date.now()) credentials = await getUploadAuthV5Credentials(page);

Type guard

null

Try / catch

try {
  return await applyVideoUploadInner(fileSize, creds);
} catch (e) {
  const m = e.message.match(/"Code":"([^"]+)"/);
  if (m && /Expired|Signature|AuthAccess/i.test(m[1])) {
    creds = await getUploadAuthV5Credentials(page); // refresh then retry once
    return applyVideoUploadInner(fileSize, creds);
  }
  if (m && /Throttl|Limit/i.test(m[1])) { await sleep(5000); return retry(); }
  throw e;
}

Prevention

When it happens

Trigger: HTTP status >= 400 from the VOD endpoint, or a 200 response whose ResponseMetadata.Error is non-null — e.g. SignatureDoesNotMatch, InvalidParameter, quota exceeded, or auth expiry errors.

Common situations: Expired STS session token so AWS4 signing fails; wrong system clock causing signature timestamp mismatch; FileSize param rejected; upload quota exhausted for the account/space.

Related errors


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