decolua/9router · warning
Invalid JSON body
Error message
Invalid JSON body
What it means
handleSearch (src/sse/handlers/search.js:28) parses the request body with request.json() inside a try/catch; any parse failure returns HTTP 400 'Invalid JSON body'. The endpoint requires a JSON document with provider/model and query fields, so a malformed body is rejected before any auth or routing happens. This is a client-side request formatting problem, not a server fault.
Source
Thrown at src/sse/handlers/search.js:28
import { handleSearchCore } from "open-sse/handlers/search/index.js";
import { errorResponse, unavailableResponse } from "open-sse/utils/error.js";
import { HTTP_STATUS } from "open-sse/config/runtimeConfig.js";
import * as log from "../utils/logger.js";
import { updateProviderCredentials, checkAndRefreshToken } from "../services/tokenRefresh.js";
import { handleComboChat, getComboModelsFromData } from "open-sse/services/combo.js";
/**
* Handle web search request for the SSE/Next.js server.
* Provider IS the model (no model field). Mirrors handleEmbeddings auth + fallback flow.
*
* @param {Request} request
*/
export async function handleSearch(request) {
let body;
try {
body = await request.json();
} catch {
log.warn("SEARCH", "Invalid JSON body");
return errorResponse(HTTP_STATUS.BAD_REQUEST, "Invalid JSON body");
}
const url = new URL(request.url);
// Accept either `provider` or `model` (UI sends `model` since provider IS the model for webSearch)
const providerInput = body.provider || body.model;
const query = body.query;
log.request("POST", `${url.pathname} | ${providerInput}`);
// 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)");
}
View on GitHub (pinned to 90b52e06ff)
Solutions
- Send a valid JSON object body, e.g. {"provider":"exa","query":"..."} or {"model":"...","query":"..."}.
- Set the Content-Type: application/json header on the request.
- Validate the JSON with JSON.parse (or a linter) on the client before sending.
- Check for double-serialization: if your payload looks like '{\"provider\"...' when logged, un-nest the string.
- Inspect the raw body actually received server-side (proxy logs) to catch middleware or gateway corruption.
Example fix
// before: body is double-stringified / wrong type
fetch(base + '/v1/search', { method: 'POST', body: JSON.stringify(JSON.stringify(payload)) });
// after
fetch(base + '/v1/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ provider: 'exa', query: 'langchain error handling' })
}); Defensive patterns
Strategy: validation
Validate before calling
const payload = { provider: 'exa', query: 'test' };
const bodyText = JSON.stringify(payload);
JSON.parse(bodyText); // throws locally if you accidentally serialized twice
if (!payload.provider || !payload.query) throw new Error('provider and query are required'); Type guard
function isSearchPayload(v) {
return typeof v === 'object' && v !== null
&& typeof (v.provider ?? v.model) === 'string'
&& typeof v.query === 'string' && v.query.trim().length > 0;
} Try / catch
let body;
try { body = await res.json(); } catch { throw new Error('Non-JSON response — check request encoding and endpoint'); } Prevention
- Always set Content-Type: application/json on POSTs to /v1 endpoints.
- Log the exact body string once during integration to catch double-stringification early.
- Validate payloads with a schema (zod/ajv) before sending.
- Beware proxies/middleware that rewrite or compress request bodies.
When it happens
Trigger: POST to the /v1 search endpoint with a body that request.json() cannot parse: empty body, HTML/plain-text content, truncated JSON, or an unparseable content-type/body combination (e.g. form-encoded or raw bytes).
Common situations: A script sending JSON.stringify on an already-stringified string or concatenating objects; a proxy/gateway mangling or truncating the payload; forgetting 'Content-Type: application/json' while a client library then encodes the body differently; curl with -d instead of --data and no proper quoting; a file uploaded raw instead of read-and-parsed.
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
- Invalid JSON body
- Invalid JSON body
- Invalid JSON body
- Input must be a JSON object or array of objects
- CLIProxyAPI auth JSON is invalid
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/f1ab8f1c426f257a.
Report an issue: GitHub.