transloadit/uppy · error · Error

${C.ERROR_PREFIX}CompleteMultipartUpload response missing Lo

Error message

${C.ERROR_PREFIX}CompleteMultipartUpload response missing Location or Key: ${JSON.stringify(r)}

What it means

completeMultipartUpload parses S3's XML response; if the parsed result lacks Location and Key fields the client can't build a usable upload result, so it throws. This typically means the 200 response actually contained an error payload (S3 can return errors inside a 200 for this call).

Source

Thrown at packages/@uppy/aws-s3/src/s3-client/S3mini.ts:452

    if (parsed && typeof parsed === 'object') {
      // Check for both cases (camelCase from our parser, PascalCase from S3)
      const result =
        parsed.completeMultipartUploadResult ||
        parsed.CompleteMultipartUploadResult ||
        parsed

      if (result && typeof result === 'object') {
        const r = result as Record<string, unknown>

        // S3 returns PascalCase (Location, Bucket, Key, ETag).
        // Normalize to lowercase for our type interface.
        const resultLocation = (r.Location || r.location) as string | undefined
        const resultBucket = (r.Bucket || r.bucket) as string | undefined
        const resultKey = (r.Key || r.key) as string | undefined
        const rawEtag = (r.ETag || r.eTag || r.etag) as string | undefined

        if (!resultLocation || !resultKey) {
          throw new Error(
            `${C.ERROR_PREFIX}CompleteMultipartUpload response missing Location or Key: ${JSON.stringify(r)}`,
          )
        }

        const etag = rawEtag ? U.sanitizeXmlETag(rawEtag) : undefined

        return {
          location: resultLocation,
          bucket: resultBucket,
          key: resultKey,
          etag,
        }
      }
    }

    throw new Error(
      `${C.ERROR_PREFIX}Failed to complete multipart upload: ${JSON.stringify(
        parsed,

View on GitHub (pinned to 5d4dedd02a)

Solutions

  1. Retry completeMultipartUpload after a short delay (S3 eventual consistency on part listing)
  2. Log JSON.stringify(r) to see the actual payload; if it contains an Error code, address that (e.g. InvalidPart)
  3. Ensure all parts and ETags were sent correctly in the complete request
  4. Verify a standard S3-compatible endpoint is used and not mangling responses

Example fix

// before
await s3Mini.completeMultipartUpload({ key, uploadId, parts })

// after
try {
  await s3Mini.completeMultipartUpload({ key, uploadId, parts })
} catch (err) {
  if (String(err.message).includes('response missing Location or Key')) {
    await delay(1000)
    return s3Mini.completeMultipartUpload({ key, uploadId, parts })
  }
  throw err
}
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

null

Try / catch

try { await complete(...) } catch (e) { if (/response missing Location or Key/.test(String(e?.message))) { await delay(1000); return complete(...) } throw e }

Prevention

When it happens

Trigger: Completing a multipart upload where S3 returns 200 with an <Error> body (e.g. InternalError or slow part propagation), or an unexpected/empty response body that fails XML parsing into the expected fields.

Common situations: Completing immediately after uploading the last part; transient S3 internal errors; custom endpoints/proxies that mangle the XML response.

Related errors


AI-assisted analysis of transloadit/uppy@5d4dedd02a (2026-08-28). Data as JSON: /api/errors/76aabeb60f00e80b. Report an issue: GitHub.