jackwener/OpenCLI · critical · CommandExecutionError

TOS init multipart upload failed with status ${res.status}:

Error message

TOS init multipart upload failed with status ${res.status}: ${res.body}

What it means

initMultipartUpload starts a TOS (Toutiao Object Storage) multipart upload via the gateway and throws this CommandExecutionError when the POST init-phase request returns a non-200 HTTP status. It signals the upload session could not be created at all, so no parts can be uploaded.

Source

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

            || json?.UploadId
            || json?.uploadID
            || json?.uploadId
            || null;
    }
    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',
    };

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-fetch fresh upload authorization (getUploadAuthV5Credentials / upload info) and retry the upload
  2. Check the douyin session is logged in and cookies are valid
  3. Inspect res.body in the message for the server's error reason and act on it
  4. Verify network/proxy allows POST to the TOS gateway host

Example fix

// before
await tosUpload({ filePath, uploadInfo: staleUploadInfo });
// after
const uploadInfo = await getUploadAuthV5Credentials(page); // fresh auth
await tosUpload({ filePath, uploadInfo });
Defensive patterns

Strategy: retry

Validate before calling

function canAttemptUpload(uploadInfo) {
  return Boolean(uploadInfo && uploadInfo.tos_upload_url && uploadInfo.auth && uploadInfo.upload_header && uploadInfo.user_id);
}

Type guard

function hasFreshUploadInfo(u) {
  return !!u && typeof u === 'object' && typeof u.tos_upload_url === 'string' &&
    u.tos_upload_url.startsWith('https://') && !!u.auth;
}

Try / catch

try { await tosUpload(options); }
catch (e) {
  if (String(e.message).startsWith('TOS init multipart upload failed')) {
    const fresh = await getUploadAuthV5Credentials(page);
    await tosUpload({ ...options, uploadInfo: fresh });
  } else throw e;
}

Prevention

When it happens

Trigger: tosUpload -> initMultipartUpload posts to `${tosUrl}?uploadmode=part&phase=init` and receives HTTP 4xx/5xx, typically because the auth token/upload_header from the upload authorization is expired, or the tos_upload_url/region is wrong.

Common situations: Stale uploadInfo obtained long before upload (auth expired); logged-out Douyin session; network/proxy intercepting the POST; wrong region extracted from host.

Related errors


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