Why Your OpenAI JSON Calls Randomly Fail with "could not parse JSON body" (And How to Fix It)

작성자

카테고리:

← 피드로
DEV Community · zey-netizen · 2026-09-18 개발(SW)

zey-netizen

“You’re calling OpenAI from Node.js. 99% of requests work. Then randomly:

BadRequestError: 400 could not parse JSON body

Or:

SyntaxError: Invalid JSON: EOF while parsing an object

Or, if you’re using tool calling:

Invalid JSON in tool call arguments

You check your code. Nothing changed. You retry manually — it works. You deploy again. It breaks again.

What’s actually happening

These errors are NOT your bugs. They’re transient artifacts from:

  1. Proxy / network corruption — the request body arrives at OpenAI partially mangled. OpenAI returns 400.
  2. Streaming truncation — the SDK parses JSON before the stream signals completion. Especially with max_output_tokens set low.
  3. Model-side tool call malformation — the model emits single quotes, trailing commas, or markdown code fences inside function.arguments.

Every one of these is intermittent. So you can’t debug it with a stack trace. You just suffer.

The wrong fix

People usually do this:

try {
  const res = await client.chat.completions.create({...});
} catch (e) {
  const res = await client.chat.completions.create({...});
}

Enter fullscreen mode Exit fullscreen mode

Problems:

  • You retry errors that should NOT be retried (invalid API key, rate limit that needs backoff, context length exceeded).
  • You don’t sanitize tool call args, so malformed JSON still crashes downstream.
  • You add 20 lines of retry boilerplate per call site.

The right fix: classify then act

Different errors need different handling:

Error Correct action Transient 400 (could not parse JSON body) Retry with backoff + jitter Truncated stream (EOF while parsing) Retry, possibly with higher max_tokens Malformed tool args Sanitize inline, no retry needed Invalid API key Fail fast, do NOT retry Rate limit Retry with longer backoff

Doing this manually is ~150 lines of code you’ll write badly. Or:

npm install @coder12-z/llm-shield

import { shield } from "@coder12-z/llm-shield";
import OpenAI from "openai";

const client = new OpenAI();

const safeCall = shield(async (prompt) =>
  client.chat.completions.create({
    model: "gpt-4o-mini",
    messages: [{ role: "user", content: prompt }],
    response_format: { type: "json_object" },
  }),
  {
    maxRetries: 3,
    onRetry: ({ attempt, kind }) => console.log(`retry #${attempt} (${kind})`),
  }
);

Enter fullscreen mode Exit fullscreen mode

Three lines of config. Zero dependencies. Works with OpenAI, Anthropic, Gemini, or anything that throws Error objects with status and message.

What it doesn’t do

  • It doesn’t repair arbitrary JSON strings — for that, use jsonrepair (https://www.npmjs.com/package/jsonrepair).
  • It doesn’t call LLMs for you — it wraps your existing calls.
  • No API key. No network calls. No telemetry.

Links

MIT licensed. Feedback welcome.

원문에서 계속 ↗