moeru-ai/airi · error
Invalid request body
Error message
Invalid request body
What it means
The OpenRouter audio-speech adapter intercepts the speech fetch and requires init.body to be a JSON string so it can extract 'input' (text) and 'voice'. A missing or non-string body throws this error before any network call is made.
Source
Thrown at packages/provider-inference/src/providers/cloud/openrouter-audio-speech/index.ts:95
}
}
}
return chunks
}
function decodeBase64Pcm(chunks: string[]) {
const binary = atob(chunks.join(''))
const bytes = new Uint8Array(binary.length)
for (let index = 0; index < binary.length; index++)
bytes[index] = binary.charCodeAt(index)
return bytes
}
function createAudioFetch(apiKey: string, baseUrl: string, model: string) {
return async (_input: RequestInfo | URL, init?: RequestInit) => {
if (!init?.body || typeof init.body !== 'string')
throw new Error('Invalid request body')
const body = JSON.parse(init.body) as { input?: string, voice?: string }
const response = await globalThis.fetch(new URL('chat/completions', baseUrl), {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
...OPENROUTER_ATTRIBUTION_HEADERS,
},
body: JSON.stringify({
model,
messages: [{ role: 'user', content: ttsPrompt(body.input ?? '') }],
modalities: ['text', 'audio'],
audio: { voice: body.voice, format: 'pcm16' },
stream: true,
}),
})
if (!response.ok)View on GitHub (pinned to f679616c34)
Solutions
- Use the provider through its speech API so the client sends JSON.stringify'd { input, voice }.
- If calling fetch directly, pass body: JSON.stringify({ model, input, voice, modalities: ['text','audio'] }).
- Remove or audit middleware that changes the body type.
- Confirm Content-Type is application/json on the outgoing request.
Example fix
// before
await audioFetch(url, { method: 'POST', body: new URLSearchParams({ input: text }) })
// after
await audioFetch(url, { method: 'POST', body: JSON.stringify({ input: text, voice: 'alloy' }) }) Defensive patterns
Strategy: validation
Validate before calling
const body = JSON.stringify({ input: text, voice })
if (typeof body !== 'string' || !text?.trim()) throw new TypeError('OpenRouter audio request needs a JSON string body with input') Type guard
const hasStringBody = (init?: RequestInit): init is RequestInit & { body: string } => typeof init?.body === 'string' Try / catch
try { await audioFetch(url, req) } catch (e) { if (e.message === 'Invalid request body') { /* re-issue with JSON.stringify({ input, voice }) */ } else throw e } Prevention
- Always serialize the body with JSON.stringify
- Use the provider's speech API, not raw fetch
- Avoid middleware that mutates body type
- Include required fields input and voice
When it happens
Trigger: Issuing the speech request with FormData, a ReadableStream, URLSearchParams, or no body at all instead of a serialized JSON string.
Common situations: Calling the provider's fetch manually with multipart data; an SDK/client configuration that streams bodies; wrapping the provider with middleware that transforms the request body.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Invalid request body
- Invalid speech request body
- Invalid request body
- Invalid request body
- MiMo voice clone requires a base64 audio sample in data URI
AI-assisted analysis of moeru-ai/airi@f679616c34 (2026-09-08).
Data as JSON: /api/errors/529a9c00c1cc205f.
Report an issue: GitHub.