payloadcms/payload · error

Failed to initialize multipart upload

Error message

Failed to initialize multipart upload

What it means

A plain Error thrown client-side in R2ClientUploadHandler when the initial POST to the R2 multipart endpoint (the admin handler URL built from serverURL/apiRoute/endpointPath) returns a non-ok response. It is a generic client error with no HTTP status attached, surfaced from the browser/worker fetch that starts a multipart upload.

Source

Thrown at packages/storage-r2/src/client/R2ClientUploadHandler.ts:50

    const params: R2StorageMultipartUploadHandlerParams = {
      collection: collectionSlug,
      docPrefix: sanitizedDocPrefix,
      fileName: file.name,
      fileType: file.type,
    }
    const baseURL = formatAdminURL({
      apiRoute,
      path: endpointPath,
      serverURL,
    })

    const getEndpoint = () => `${baseURL}?${String(new URLSearchParams(params))}`

    // Initialize the multipart upload.
    const multipart = await fetch(getEndpoint(), { method: 'POST' })
    if (!multipart.ok) {
      throw new Error('Failed to initialize multipart upload')
    }

    const { filename: sanitizedFilename, ...multipartUpload } = (await multipart.json()) as {
      filename?: string
    } & Pick<R2MultipartUpload, 'key' | 'uploadId'>

    if (sanitizedFilename && sanitizedFilename !== file.name) {
      updateFilename(sanitizedFilename)
    }

    const multipartUploadedParts: R2UploadedPart[] = []

    params.multipartId = multipartUpload.uploadId
    params.multipartKey = multipartUpload.key

    const partTotal = Math.ceil(file.size / chunkSize)

    for (let part = 1; part <= partTotal; part++) {

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Open the failing POST URL in the network tab and read the actual status/body — most likely 403 (auth) or 404 (route).
  2. Verify serverURL, apiRoute, and the endpoint path resolve to the mounted R2 multipart handler.
  3. Ensure the user is authenticated and the collection allows their create access.
  4. Add CORS headers / credentials so the browser fetch is not blocked.

Example fix

// before
const multipart = await fetch(getEndpoint(), { method: 'POST' })
if (!multipart.ok) {
  throw new Error('Failed to initialize multipart upload')
}

// after — surface the server's error body for diagnosis
const multipart = await fetch(getEndpoint(), { method: 'POST', credentials: 'include' })
if (!multipart.ok) {
  const body = await multipart.text().catch(() => '<no body>')
  throw new Error(`Failed to initialize multipart upload (${multipart.status}): ${body}`)
}
Defensive patterns

Strategy: try-catch

Validate before calling

function endpointLooksValid(baseURL: string): boolean {
  try {
    const u = new URL(baseURL)
    return u.protocol === 'http:' || u.protocol === 'https:'
  } catch {
    return false
  }
}

if (!endpointLooksValid(baseURL)) {
  throw new Error(`Invalid multipart endpoint URL: ${baseURL}`)
}

Type guard

function isFetchError(err: unknown): err is Error {
  return err instanceof Error && /multipart/i.test(err.message)
}

Try / catch

try {
  await handler.upload(file)
} catch (err) {
  if (err instanceof Error && /initialize multipart/i.test(err.message)) {
    // inspect last network response, re-auth, or surface retry
    showRetryUploadUI()
    return
  }
  throw err
}

Prevention

When it happens

Trigger: The initialize POST returns any non-2xx: 401/403 (access denied — see errors 382/380/381), 404 (wrong serverURL/apiRoute/endpointPath), 500 (handler threw), or a network/CORS failure yielding response.ok === false.

Common situations: Misconfigured serverURL or apiRoute producing a 404; CORS not allowing the POST from the browser; the user's session expired so the handler returns Forbidden; reverse proxy rewriting the endpoint path; the R2 adapter handler is not mounted at the expected route.

Related errors


AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12). Data as JSON: /api/errors/144119abdeb3140d. Report an issue: GitHub.