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

  1. Serialize the body to a JSON string (or Uint8Array/ArrayBuffer) before passing it to the signed fetch.
  2. If you have a Blob, read it into an ArrayBuffer first (await blob.arrayBuffer()) and pass that.
  3. If the body is a stream, buffer it fully into a Uint8Array before signing — SigV4 requires the complete payload.
  4. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/19bd84425c997145. Report an issue: GitHub.