can1357/oh-my-pi · error · TypeError
Cannot SigV4-sign ${init.body.constructor?.name ?? typeof in
Error message
Cannot SigV4-sign ${init.body.constructor?.name ?? typeof init.body} request body What it means
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.
Source
Thrown at packages/ai/src/providers/bedrock-mantle.ts:20
import type { FetchImpl, Model } from "../types";
import { resolveAwsRegion } from "../utils/aws-profile";
import { invalidateAwsCredentialCache, resolveAwsCredentials } from "./aws-credentials";
import { signRequest } from "./aws-sigv4";
import type { OpenAIResponsesOptions } from "./openai-responses";
import { NO_AUTH_SENTINEL } from "./openai-shared";
export type BedrockMantleProviderOptions = AwsBedrockProviderOptions;
export interface BedrockMantleOptions extends OpenAIResponsesOptions {
providerOptions?: BedrockMantleProviderOptions;
}
async function requestBody(input: string | URL | Request, init?: RequestInit): Promise<Uint8Array> {
if (init?.body !== undefined && init.body !== null) {
if (typeof init.body === "string") return new TextEncoder().encode(init.body);
if (init.body instanceof Uint8Array) return init.body;
if (init.body instanceof ArrayBuffer) return new Uint8Array(init.body);
throw new TypeError(`Cannot SigV4-sign ${init.body.constructor?.name ?? typeof init.body} request body`);
}
if (input instanceof Request) return new Uint8Array(await input.clone().arrayBuffer());
return new Uint8Array();
}
function createSignedFetch(options: BedrockMantleOptions, region: string): FetchImpl {
const baseFetch = options.fetch ?? (globalThis.fetch as FetchImpl);
const signedFetch = async (input: string | URL | Request, init?: RequestInit): Promise<Response> => {
const url = new URL(input instanceof Request ? input.url : input.toString());
const method = init?.method ?? (input instanceof Request ? input.method : "POST");
const headers = new Headers(input instanceof Request ? input.headers : undefined);
for (const [name, value] of new Headers(init?.headers)) headers.set(name, value);
headers.delete("authorization");
const body = await requestBody(input, init);
const credentials = await resolveAwsCredentials({
profile: options.providerOptions?.profile,
region,
signal: options.signal,View on GitHub (pinned to 9690622007)
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.
Example fix
// before
signedFetch(url, { method: "POST", body: formData });
// after
signedFetch(url, {
method: "POST",
body: JSON.stringify(payload), // string body is SigV4-signable
headers: { "content-type": "application/json" },
}); Defensive patterns
Strategy: validation
Validate before calling
function assertSignableBody(body: unknown): asserts body is string | Uint8Array | ArrayBuffer | undefined | null {
if (body == null) return;
if (typeof body !== "string" && !(body instanceof Uint8Array) && !(body instanceof ArrayBuffer)) {
throw new Error(`SigV4 signing requires string/Uint8Array/ArrayBuffer body, got ${(body as object).constructor?.name}`);
}
} Type guard
function isSignableBody(body: unknown): body is string | Uint8Array | ArrayBuffer {
return typeof body === "string" || body instanceof Uint8Array || body instanceof ArrayBuffer;
} Try / catch
try {
await streamBedrockMantle(model, ctx, options);
} catch (err) {
if (err instanceof TypeError && err.message.includes("Cannot SigV4-sign")) {
// serialize the body and retry once
} else throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Bedrock HTTP ${response.status}: ${errBody.slice(0, 1000)}
- Bedrock response has no body
- exceptionType
- code
- Unable to resolve AWS credentials. Configure static environm
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/19bd84425c997145.
Report an issue: GitHub.