heygen-com/hyperframes · error

Failed to prepare project upload

Error message

Failed to prepare project upload

What it means

publishProjectArchiveStaged: the initial POST to /v1/hyperframes/projects/publish/upload returned a non-OK HTTP status that is NOT 404 or 405 (those signal 'staged uploads unsupported' and cause a fallback to direct publish, returning null — not an error). So this fires on 400/401/403/500/etc. from the upload-preparation endpoint, OR when the response parsed but parseStagedUploadResponse returned null (missing upload_url or upload_key). readErrorMessage extracts the server message. This is the metadata stage of staged publishing, before any bytes are uploaded.

Source

Thrown at packages/cli/src/utils/publishProject.ts:640

      }),
      headers: {
        ...authHeaders,
        "content-type": "application/json",
      },
      signal: AbortSignal.timeout(PUBLISH_METADATA_TIMEOUT_MS),
    }),
    "Failed to prepare project upload",
    PUBLISH_TRANSPORT_ATTEMPTS,
  );

  if (uploadResponse.status === 404 || uploadResponse.status === 405) {
    return null;
  }

  const uploadPayload = await readJson(uploadResponse);
  const stagedUpload = parseStagedUploadResponse(uploadPayload, archive.buffer.byteLength);
  if (!uploadResponse.ok || !stagedUpload) {
    throw new Error(await readErrorMessage(uploadResponse, "Failed to prepare project upload"));
  }

  await uploadArchiveToPresignedUrl(stagedUpload, archive);

  const completeResponse = await fetchForPublish(
    `${apiBaseUrl}/v1/hyperframes/projects/publish/complete`,
    () => ({
      method: "POST",
      body: JSON.stringify({
        upload_key: stagedUpload.uploadKey,
        file_name: fileName,
        title,
        ...(isPublic ? { is_public: true } : {}),
        ...(projectId ? { project_id: projectId } : {}),
      }),
      headers: {
        ...authHeaders,
        "content-type": "application/json",

View on GitHub (pinned to c2996c8626)

Solutions

  1. Read the embedded server message — it names the specific rejection reason.
  2. For 401/403, re-authenticate and retry.
  3. For 400, ensure content_type is application/zip and content_length matches the archive byte length.
  4. For 5xx, retry after a short wait.
  5. If staged uploads are genuinely unsupported, the server should return 404/405 (which fall back to direct publish); a 500 here is a server bug worth reporting.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  return await publishProjectArchive(projectDir, opts);
} catch (err) {
  if (err instanceof Error && /Failed to prepare project upload/.test(err.message)) {
    // err.message includes server text; 401 -> re-auth, 5xx -> retry, else report
    console.error('Staged upload prep failed:', err.message);
  } else throw err;
}

Prevention

When it happens

Trigger: Auth failure (401) on the staged-upload endpoint; rate limiting (429); the server rejects the content_type or content_length declared in the JSON body; a server bug returns 200 but omits upload_url/upload_key from the payload (parseStagedUploadResponse -> null); the staged-upload feature is partially deployed (returns 500 instead of 404).

Common situations: Expired credentials; a content_type the server does not accept; a content_length mismatch (archive changed between build and the upload-prep request); server-side incident affecting only the staged-upload endpoint; an anonymous publish hitting a staged-upload endpoint that requires auth.

Related errors


AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12). Data as JSON: /api/errors/eb258105714d1d73. Report an issue: GitHub.