{"record":{"id":"3bde8f5bcc6d4883","repo":"decolua/9router","slug":"invalid-json-body-3bde8f","errorCode":null,"errorMessage":"Invalid JSON body","messagePattern":"Invalid JSON body","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"src/sse/handlers/embeddings.js","lineNumber":37,"sourceCode":"  const promptTokens = raw.prompt_tokens ?? raw.input_tokens;\n  const completionTokens = raw.completion_tokens ?? raw.output_tokens ?? 0;\n  const totalTokens = raw.total_tokens;\n  if (!Number.isSafeInteger(promptTokens) || promptTokens <= 0 || completionTokens !== 0 || totalTokens !== promptTokens) return null;\n  return { prompt_tokens: promptTokens, completion_tokens: 0, total_tokens: totalTokens };\n}\n\n/**\n * Handle embeddings request for the SSE/Next.js server.\n * Follows the same auth + fallback pattern as handleChat.\n *\n * @param {Request} request\n */\nexport async function handleEmbeddings(request) {\n  let body;\n  try {\n    body = await request.json();\n  } catch {\n    log.warn(\"EMBEDDINGS\", \"Invalid JSON body\");\n    return errorResponse(HTTP_STATUS.BAD_REQUEST, \"Invalid JSON body\");\n  }\n\n  const url = new URL(request.url);\n  const modelStr = body.model;\n\n  log.request(\"POST\", `${url.pathname} | ${modelStr}`);\n\n  // Log API key (masked)\n  const apiKey = extractApiKey(request);\n  if (apiKey) {\n    log.debug(\"AUTH\", `API Key: ${log.maskKey(apiKey)}`);\n  } else {\n    log.debug(\"AUTH\", \"No API key provided (local mode)\");\n  }\n\n  // Enforce API key if enabled in settings\n  const settings = await getSettings();","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/sse/handlers/embeddings.js#L19-L55","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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)."],"exampleFix":"// before\nconst res = await fetch(url, { method: 'POST', body: { input: texts } });\n\n// after\nconst res = await fetch(url, {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ model: 'openai/text-embedding-3-small', input: texts })\n});","handlingStrategy":"validation","validationCode":"const payload = JSON.stringify({ model, input });\nJSON.parse(payload); // throws locally if something is wrong before hitting /v1/embeddings\nif (!Array.isArray(input) && typeof input !== 'string') throw new TypeError('input must be string or array');","typeGuard":"function isValidEmbeddingsBody(body) {\n  return Boolean(body) && typeof body === 'object' &&\n    typeof body.model === 'string' &&\n    (typeof body.input === 'string' || Array.isArray(body.input));\n}","tryCatchPattern":"const res = await fetch(embedUrl, opts);\nif (res.status === 400 && (await res.text()).includes('Invalid JSON body')) {\n  console.error('embeddings payload not valid JSON:', opts.body);\n}","preventionTips":["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."],"tags":["http","bad-request","json","embeddings"],"backgroundTag":"invalid-json-body","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}