{"record":{"id":"19bd84425c997145","repo":"can1357/oh-my-pi","slug":"cannot-sigv4-sign-init-body-constructor-name","errorCode":null,"errorMessage":"Cannot SigV4-sign ${init.body.constructor?.name ?? typeof init.body} request body","messagePattern":"Cannot SigV4-sign (.+?) request body","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/providers/bedrock-mantle.ts","lineNumber":20,"sourceCode":"import type { FetchImpl, Model } from \"../types\";\nimport { resolveAwsRegion } from \"../utils/aws-profile\";\nimport { invalidateAwsCredentialCache, resolveAwsCredentials } from \"./aws-credentials\";\nimport { signRequest } from \"./aws-sigv4\";\nimport type { OpenAIResponsesOptions } from \"./openai-responses\";\nimport { NO_AUTH_SENTINEL } from \"./openai-shared\";\n\nexport type BedrockMantleProviderOptions = AwsBedrockProviderOptions;\n\nexport interface BedrockMantleOptions extends OpenAIResponsesOptions {\n\tproviderOptions?: BedrockMantleProviderOptions;\n}\n\nasync function requestBody(input: string | URL | Request, init?: RequestInit): Promise<Uint8Array> {\n\tif (init?.body !== undefined && init.body !== null) {\n\t\tif (typeof init.body === \"string\") return new TextEncoder().encode(init.body);\n\t\tif (init.body instanceof Uint8Array) return init.body;\n\t\tif (init.body instanceof ArrayBuffer) return new Uint8Array(init.body);\n\t\tthrow new TypeError(`Cannot SigV4-sign ${init.body.constructor?.name ?? typeof init.body} request body`);\n\t}\n\tif (input instanceof Request) return new Uint8Array(await input.clone().arrayBuffer());\n\treturn new Uint8Array();\n}\n\nfunction createSignedFetch(options: BedrockMantleOptions, region: string): FetchImpl {\n\tconst baseFetch = options.fetch ?? (globalThis.fetch as FetchImpl);\n\tconst signedFetch = async (input: string | URL | Request, init?: RequestInit): Promise<Response> => {\n\t\tconst url = new URL(input instanceof Request ? input.url : input.toString());\n\t\tconst method = init?.method ?? (input instanceof Request ? input.method : \"POST\");\n\t\tconst headers = new Headers(input instanceof Request ? input.headers : undefined);\n\t\tfor (const [name, value] of new Headers(init?.headers)) headers.set(name, value);\n\t\theaders.delete(\"authorization\");\n\t\tconst body = await requestBody(input, init);\n\t\tconst credentials = await resolveAwsCredentials({\n\t\t\tprofile: options.providerOptions?.profile,\n\t\t\tregion,\n\t\t\tsignal: options.signal,","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/providers/bedrock-mantle.ts#L2-L38","documentation":"The Bedrock Mantle provider signs requests with AWS SigV4, which requires the raw request body bytes. requestBody() can only convert string, Uint8Array, or ArrayBuffer bodies; any other body type (Blob, FormData, ReadableStream, Buffer-like objects) cannot be deterministically hashed for signing, so it throws a TypeError naming the offending constructor.","triggerScenarios":"Passing a RequestInit whose body is a Blob, FormData, URLSearchParams, ReadableStream, or other non-string/non-binary type into the signed fetch wrapper used by the Bedrock Mantle provider; the error message shows the body's constructor name.","commonSituations":"Generic fetch middleware that attaches FormData for uploads being reused for Bedrock; a library upgrade that started passing ReadableStream bodies; hand-rolled client code assuming full fetch body support.","solutions":["Serialize the body to a JSON string (or Uint8Array/ArrayBuffer) before passing it to the signed fetch.","If you have a Blob, read it into an ArrayBuffer first (await blob.arrayBuffer()) and pass that.","If the body is a stream, buffer it fully into a Uint8Array before signing — SigV4 requires the complete payload.","Avoid reuse of generic fetch wrappers that set FormData/URLSearchParams bodies with this provider."],"exampleFix":"// before\nsignedFetch(url, { method: \"POST\", body: formData });\n// after\nsignedFetch(url, {\n  method: \"POST\",\n  body: JSON.stringify(payload), // string body is SigV4-signable\n  headers: { \"content-type\": \"application/json\" },\n});","handlingStrategy":"validation","validationCode":"function assertSignableBody(body: unknown): asserts body is string | Uint8Array | ArrayBuffer | undefined | null {\n  if (body == null) return;\n  if (typeof body !== \"string\" && !(body instanceof Uint8Array) && !(body instanceof ArrayBuffer)) {\n    throw new Error(`SigV4 signing requires string/Uint8Array/ArrayBuffer body, got ${(body as object).constructor?.name}`);\n  }\n}","typeGuard":"function isSignableBody(body: unknown): body is string | Uint8Array | ArrayBuffer {\n  return typeof body === \"string\" || body instanceof Uint8Array || body instanceof ArrayBuffer;\n}","tryCatchPattern":"try {\n  await streamBedrockMantle(model, ctx, options);\n} catch (err) {\n  if (err instanceof TypeError && err.message.includes(\"Cannot SigV4-sign\")) {\n    // serialize the body and retry once\n  } else throw err;\n}","preventionTips":["Always send JSON-stringified bodies to Bedrock Mantle.","Never reuse fetch wrappers that attach FormData/ReadableStream bodies with this provider.","Buffer any streaming payloads completely before signing."],"tags":["aws","sigv4","request-body","typeerror","bedrock"],"backgroundTag":"sigv4-unsupported-body-type","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}