linshenkx/prompt-optimizer · error · Error

S3 download returned an unsupported response body

Error message

S3 download returned an unsupported response body

What it means

Thrown by s3BodyToArrayBuffer when the S3 (or S3-compatible) response body cannot be coerced to bytes: it handles empty bodies, ArrayBuffer, Uint8Array, and streaming chunked bodies, and throws this error for anything else (e.g. a Blob or string body).

Source

Thrown at packages/ui/src/utils/remote-backup.ts:701

    const chunks: Uint8Array[] = []
    let total = 0
    for (;;) {
      const { done, value } = await reader.read()
      if (done) break
      if (!value) continue
      chunks.push(value)
      total += value.byteLength
    }
    const bytes = new Uint8Array(total)
    let offset = 0
    for (const chunk of chunks) {
      bytes.set(chunk, offset)
      offset += chunk.byteLength
    }
    return bytes.buffer
  }

  throw new Error('S3 download returned an unsupported response body')
}

const isS3NotFoundError = (error: unknown): boolean => {
  const value = error as {
    name?: string
    Code?: string
    code?: string
    $metadata?: { httpStatusCode?: number }
  }
  return value?.$metadata?.httpStatusCode === 404 ||
    value?.name === 'NotFound' ||
    value?.name === 'NoSuchKey' ||
    value?.Code === 'NoSuchKey' ||
    value?.code === 'NoSuchKey'
}

const s3ErrorMessage = (error: unknown): string =>
  (error as Error)?.message || String(error)

View on GitHub (pinned to 3e677b1d9f)

Solutions

  1. Inspect the actual body type at runtime (body?.constructor?.name) to identify the unexpected shape
  2. Convert before calling: use response.arrayBuffer() for fetch responses, orSdkStreamMixin/transform in AWS SDK v3 to normalize Body
  3. Pin or upgrade the S3 SDK to a version whose body shape the helper supports
  4. If using an S3-compatible gateway, ensure it returns standard binary octet-stream responses

Example fix

// before
const body = await s3.send(new GetObjectCommand({ Key })).Body // unknown shape
// after - normalize fetch-style responses first
const resp = await fetch(signedUrl)
const body = await resp.arrayBuffer() // ArrayBuffer is always accepted
const buf = await s3BodyToArrayBuffer(body)
Defensive patterns

Strategy: type-guard

Validate before calling

const normalizeS3Body = async (body: unknown): Promise<ArrayBuffer | Uint8Array | null> => {
  if (!body) return null
  if (body instanceof Blob) return await body.arrayBuffer()
  if (typeof body === 'string') return new TextEncoder().encode(body)
  return (body as ArrayBuffer | Uint8Array) ?? null
}

Type guard

const isSupportedS3Body = (
  body: unknown,
): body is ArrayBuffer | Uint8Array =>
  body instanceof ArrayBuffer || body instanceof Uint8Array

Try / catch

try {
  const buf = await s3BodyToArrayBuffer(body)
} catch (error) {
  if ((error as Error).message.includes('unsupported response body')) {
    const resp = (body as unknown as { transformToByteArray?: () => Promise<Uint8Array> })
    if (typeof resp.transformToByteArray === 'function') {
      return await s3BodyToArrayBuffer(await resp.transformToByteArray())
    }
  }
  throw error
}

Prevention

When it happens

Trigger: S3 client (or custom endpoint/proxy like MinIO, R2, or an API gateway) returning the object body as a Blob, ReadableStream of non-Uint8Array chunks, string, or an SDK version whose body type is not handled; head requests or pre-signed URL fetches returning unexpected representations.

Common situations: Switching S3 SDK versions where getObject().Body changed shape (v2 stream vs v3 sdkStreamMixin); browser fetch returning a Blob because response.blob() was called instead of arrayBuffer(); S3-compatible backends (MinIO/Ceph) with differing response encodings; middleware converting streams.

Related errors


AI-assisted analysis of linshenkx/prompt-optimizer@3e677b1d9f (2026-08-27). Data as JSON: /api/errors/e5163201e9abed60. Report an issue: GitHub.