jackwener/OpenCLI · critical · CommandExecutionError

TOS init response missing UploadId: ${res.body}

Error message

TOS init response missing UploadId: ${res.body}

What it means

After a 200 init response, initMultipartUpload extracts the UploadId via extractUploadId; this CommandExecutionError is thrown when the response body contains no UploadId. Without an uploadId the multipart session cannot proceed.

Source

Thrown at clis/douyin/_shared/tos-upload.js:206

    }
    catch {
        return null;
    }
}
// ── Phase 1: Init multipart upload ───────────────────────────────────────────
async function initMultipartUpload(tosUrl, auth, uploadHeader, userId) {
    const initUrl = `${gatewayBaseUrl(tosUrl)}?uploadmode=part&phase=init`;
    const res = await tosRequest({
        method: 'POST',
        url: initUrl,
        headers: gatewayHeaders(auth, uploadHeader, userId),
    });
    if (res.status !== 200) {
        throw new CommandExecutionError(`TOS init multipart upload failed with status ${res.status}: ${res.body}`, 'Check that TOS upload authorization is valid and not expired.');
    }
    const uploadId = extractUploadId(res.body);
    if (!uploadId) {
        throw new CommandExecutionError(`TOS init response missing UploadId: ${res.body}`);
    }
    return uploadId;
}
// ── Phase 2: Upload a single part ────────────────────────────────────────────
async function uploadPart(tosUrl, partNumber, uploadId, data, auth, uploadHeader, userId) {
    const crc32 = crc32Hex(data);
    const url = `${gatewayBaseUrl(tosUrl)}?uploadid=${encodeURIComponent(uploadId)}&part_number=${partNumber}&phase=transfer`;
    const headers = {
        ...gatewayHeaders(auth, uploadHeader, userId),
        'Content-CRC32': crc32,
        'Content-Type': 'application/octet-stream',
        'X-Use-Init-Upload-Optimize': '1',
        'X-Use-Large-Local-Cache': '1',
    };
    const res = await tosRequest({ method: 'POST', url, headers, body: data });
    let parsed;
    try {
        parsed = JSON.parse(res.body);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Log the response body from the error message to see what was actually returned
  2. Re-acquire upload authorization and retry (200-but-invalid usually means auth/session issue)
  3. Check for Douyin/TOS API schema changes and update extractUploadId parsing
  4. Bypass proxies/CDN that may rewrite the response

Example fix

// before
const uploadId = extractUploadId(res.body); // may be undefined
// after
if (!uploadId) {
  const uploadInfo = await getUploadAuthV5Credentials(page);
  return retryInit(uploadInfo);
}
Defensive patterns

Strategy: try-catch

Type guard

function looksLikeTosInitBody(body) {
  return typeof body === 'string' && (body.includes('upload_id') || body.includes('UploadId'));
}

Try / catch

try { uploadId = await initMultipartUpload(tosUrl, auth, uploadHeader, userId); }
catch (e) {
  if (String(e.message).includes('missing UploadId')) {
    console.error('TOS init returned unexpected body; refresh auth and retry');
    uploadId = await initMultipartUpload(tosUrl, freshAuth, freshHeader, userId);
  } else throw e;
}

Prevention

When it happens

Trigger: The TOS init endpoint returned HTTP 200 but a body in an unexpected shape (HTML error page, JSON without upload_id, auth-warned payload), so extractUploadId finds nothing.

Common situations: Gateway changed response schema; request authenticated as logged-out user so a 200 HTML page is returned; CDN/proxy replaced the JSON body.

Related errors


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