decolua/9router · error
Invalid JSON body
Error message
Invalid JSON body
What it means
The embeddings endpoint (/v1/embeddings) could not parse the request body as JSON. handleEmbeddings wraps request.json() in try/catch and returns 400 'Invalid JSON body' on any parse error, mirroring the chat handler.
Source
Thrown at src/sse/handlers/embeddings.js:37
const promptTokens = raw.prompt_tokens ?? raw.input_tokens;
const completionTokens = raw.completion_tokens ?? raw.output_tokens ?? 0;
const totalTokens = raw.total_tokens;
if (!Number.isSafeInteger(promptTokens) || promptTokens <= 0 || completionTokens !== 0 || totalTokens !== promptTokens) return null;
return { prompt_tokens: promptTokens, completion_tokens: 0, total_tokens: totalTokens };
}
/**
* Handle embeddings request for the SSE/Next.js server.
* Follows the same auth + fallback pattern as handleChat.
*
* @param {Request} request
*/
export async function handleEmbeddings(request) {
let body;
try {
body = await request.json();
} catch {
log.warn("EMBEDDINGS", "Invalid JSON body");
return errorResponse(HTTP_STATUS.BAD_REQUEST, "Invalid JSON body");
}
const url = new URL(request.url);
const modelStr = body.model;
log.request("POST", `${url.pathname} | ${modelStr}`);
// Log API key (masked)
const apiKey = extractApiKey(request);
if (apiKey) {
log.debug("AUTH", `API Key: ${log.maskKey(apiKey)}`);
} else {
log.debug("AUTH", "No API key provided (local mode)");
}
// Enforce API key if enabled in settings
const settings = await getSettings();View on GitHub (pinned to 90b52e06ff)
Solutions
- JSON.stringify the payload and set Content-Type: application/json before sending to /v1/embeddings.
- Validate the body with JSON.parse client-side to catch syntax issues early.
- Check shell quoting if using curl for embedding texts (special characters break quoting).
- Ensure the body is sent as UTF-8 without BOM and not compressed unexpectedly (Content-Encoding).
Example fix
// before
const res = await fetch(url, { method: 'POST', body: { input: texts } });
// after
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ model: 'openai/text-embedding-3-small', input: texts })
}); Defensive patterns
Strategy: validation
Validate before calling
const payload = JSON.stringify({ model, input });
JSON.parse(payload); // throws locally if something is wrong before hitting /v1/embeddings
if (!Array.isArray(input) && typeof input !== 'string') throw new TypeError('input must be string or array'); Type guard
function isValidEmbeddingsBody(body) {
return Boolean(body) && typeof body === 'object' &&
typeof body.model === 'string' &&
(typeof body.input === 'string' || Array.isArray(body.input));
} Try / catch
const res = await fetch(embedUrl, opts);
if (res.status === 400 && (await res.text()).includes('Invalid JSON body')) {
console.error('embeddings payload not valid JSON:', opts.body);
} Prevention
- Serialize with JSON.stringify and send UTF-8 without BOM.
- Test embedding payloads (long/special-char inputs) with JSON.parse first.
- Watch for proxies changing Content-Encoding on large embedding batches.
- Use the provider SDK's embeddings client where possible instead of raw fetch.
When it happens
Trigger: POST to /v1/embeddings with a body that is not valid JSON: empty body, malformed syntax, wrong encoding, or a non-JSON payload (text/FormData/binary).
Common situations: Scripts calling embeddings with hand-built payloads and quoting bugs; clients sending UTF-16/BOM-encoded bodies the JSON parser rejects; proxies mangling the body; forgetting JSON.stringify when using fetch/undici directly.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/3bde8f5bcc6d4883.
Report an issue: GitHub.