decolua/9router · error
Invalid JSON body
Error message
Invalid JSON body
What it means
HTTP 400 returned by handleFetch when request.json() throws — the body is not valid JSON (empty body, malformed JSON, or a non-JSON content type). The handler parses the body first and fails fast before any auth or validation.
Source
Thrown at src/sse/handlers/fetch.js:29
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";
import { assertPublicUrl } from "@/shared/utils/ssrfGuard.js";
/**
* Handle web fetch (URL extraction) request for the SSE/Next.js server.
* Provider IS the model. Mirrors handleEmbeddings auth + fallback flow.
*
* @param {Request} request
*/
export async function handleFetch(request) {
let body;
try {
body = await request.json();
} catch {
log.warn("FETCH", "Invalid JSON body");
return errorResponse(HTTP_STATUS.BAD_REQUEST, "Invalid JSON body");
}
const reqUrl = new URL(request.url);
// Accept either `provider` or `model` (UI sends `model` since provider IS the model for webFetch)
const providerInput = body.provider || body.model;
const targetUrl = body.url;
const format = body.format;
const maxCharacters = body.max_characters;
log.request("POST", `${reqUrl.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 body with Content-Type: application/json
- Validate the payload with JSON.stringify before sending
- If using curl, quote the JSON: -d '{"provider":"x","url":"https://..."}'
- Confirm no middleware/proxy is transforming or dropping the body
Example fix
// before
curl -X POST http://localhost:20128/v1/fetch -d provider=jina url=https://example.com
// after
curl -X POST http://localhost:20128/v1/fetch -H 'Content-Type: application/json' -d '{"provider":"jina","url":"https://example.com"}' Defensive patterns
Strategy: validation
Validate before calling
const payload = JSON.stringify({ provider, url });
JSON.parse(payload); // fail fast locally before sending
await post('/v1/fetch', payload, { headers: { 'Content-Type': 'application/json' } }); Type guard
function isSerializable(x) {
try { JSON.stringify(x); return true; } catch { return false; }
} Try / catch
const res = await post('/v1/fetch', body);
if (res.status === 400 && (await res.text()).includes('Invalid JSON body')) {
console.error('Request body was not parseable JSON — log the raw body and Content-Type');
} Prevention
- Always JSON.stringify the body and set Content-Type: application/json
- Never send FormData/form-encoded bodies to this endpoint
- Test requests with curl + quoted JSON first
When it happens
Trigger: POST to the fetch endpoint with an empty body, trailing commas, unquoted keys, form-encoded or multipart bodies, or a body stringified incorrectly client-side.
Common situations: Using curl without --data or with unquoted JSON on the shell; forgetting Content-Type: application/json; SDK sending FormData; proxy stripping the body on redirects.
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
- Input must be a JSON object or array of objects
- CLIProxyAPI auth JSON is invalid
- Invalid JSON body
- Missing model
- Invalid model format
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/9ccaee16f9c43974.
Report an issue: GitHub.