OpenAI Function Call Invalid JSON — Fix (2026) | AI Error Hub
OpenAIFunction CallingSeverity: MediumClient-side

OpenAI Function Call Returned Invalid JSON

The model called your function but the arguments field contains malformed JSON — trailing commas, unquoted keys, incomplete objects, or truncation. Fix by enabling Structured Outputs (strict: true) for guaranteed valid JSON, plus fallback JSON repair.

Error surface: JSON.parse failureCategory: Function CallingFeature: Structured Outputs strict modeLast tested: Nov 15, 2026
Quick Fix (TL;DR)

Enable Structured Outputs by adding strict: true to your function definition. This guarantees the model returns JSON matching your schema exactly — no truncation, no drift, no invalid syntax. Requires additionalProperties: false and all fields in required. Works on GPT-4o, GPT-4o-mini, GPT-5, o-series.

The Error Symptoms You're Seeing

Client-side failures
tool_call.function.arguments = 
  '{"location": "Karachi", "units":'  ← truncated

json.JSONDecodeError: Expecting ',' delimiter: line 1 column 42

Or invalid but parseable-looking:
  '{"location": "Karachi", "units": "celsius",}'  ← trailing comma
  '{location: "Karachi"}'                          ← unquoted key
  '{"count": NaN}'                                 ← non-JSON value

Or extra content around JSON:
  'Here are the arguments: {"location": "Karachi"}'
  '```json\n{"location": "Karachi"}\n```'

What Actually Causes This Error

42%
strict mode not enabledLegacy function calling without Structured Outputs; occasional format drift.
22%
Truncation from max_tokens hitModel ran out of tokens mid-JSON generation.
15%
Complex nested schemasDeep object structures under stress from weaker models (mini, Haiku).
12%
Content-filtered mid-generationModeration cut off; incomplete JSON returned.
9%
System prompt confuses formattingInstructions asking for markdown wrap functions.

Fixes That Work (Tested Nov 2026)

1Enable Structured Outputs (strict: true)

Python · function with strict schema
from openai import OpenAI client = OpenAI() response = client.chat.completions.create( model="gpt-5", messages=[{"role": "user", "content": "What's the weather in Karachi?"}], tools=[{ "type": "function", "function": { "name": "get_weather", "description": "Get current weather for a location", "strict": True, # ← guarantees valid JSON "parameters": { "type": "object", "properties": { "location": {"type": "string"}, "units": {"type": "string", "enum": ["celsius", "fahrenheit"]} }, "required": ["location", "units"], "additionalProperties": False # ← required for strict } } }] ) # arguments is now guaranteed valid JSON matching schema import json tool_call = response.choices[0].message.tool_calls[0] args = json.loads(tool_call.function.arguments) # never fails
strict: true requirements

Every property in the object must appear in required. additionalProperties: false is mandatory. Not all JSON Schema features supported: no minLength, maxLength, pattern, format on strings. See Structured Outputs Schema Invalid for the full subset.

2Validate + Repair Fallback

Python · defense in depth
import json, json_repair def parse_arguments_safe(arguments_str): """Multi-strategy JSON parsing.""" # Strategy 1: direct parse (works with strict mode) try: return json.loads(arguments_str) except json.JSONDecodeError: pass # Strategy 2: strip markdown wrappers cleaned = arguments_str.strip() if cleaned.startswith("```"): cleaned = cleaned.split("\n", 1)[1] if cleaned.endswith("```"): cleaned = cleaned.rsplit("\n", 1)[0] try: return json.loads(cleaned) except json.JSONDecodeError: pass # Strategy 3: json_repair library (handles trailing commas, etc.) try: return json_repair.loads(cleaned) except Exception: pass # Strategy 4: raise with context for retry raise ValueError(f"Unparseable arguments: {arguments_str[:200]}")

3Retry with Feedback on Failure

Python · feedback loop
def call_function_with_retry(messages, tool, max_retries=2): for attempt in range(max_retries + 1): response = client.chat.completions.create( model="gpt-5", messages=messages, tools=[tool], tool_choice={"type": "function", "function": {"name": tool["function"]["name"]}} ) msg = response.choices[0].message if not msg.tool_calls: raise ValueError("Model didn't call function") tc = msg.tool_calls[0] try: args = json.loads(tc.function.arguments) return args except json.JSONDecodeError as e: if attempt == max_retries: raise # Feed error back messages.append({"role": "assistant", "tool_calls": msg.tool_calls}) messages.append({ "role": "tool", "tool_call_id": tc.id, "content": f"JSON parse error: {e}. Return valid JSON only." })

Preventing This Error Going Forward

  • Always use strict: true in production. Guaranteed valid JSON.
  • Include additionalProperties: false and full required. Mandatory for strict.
  • Simplify schemas. Deep nesting stresses even good models.
  • Set max_tokens generously. Truncation = invalid JSON.
  • Add fallback json_repair. Defense in depth for legacy code.
  • Log tool_call.function.arguments raw. Post-mortem debugging.
SK
Tested by Sana K.
Researcher · AI Error Hub
Last verified: Nov 15, 2026 · SDK: openai 1.54

Frequently Asked Questions

GPT-4o (Aug 2024+), GPT-4o-mini, GPT-5, GPT-5-mini, o-series (o1, o3). Older models like gpt-3.5-turbo don't support strict. Check the model reference in OpenAI docs.

First request with a new schema: ~1-2s schema compilation. Subsequent requests with same schema: no overhead. Compilation is cached per schema per organization for a period.

Yes — combine freely. tool_choice: "auto", "required", or specific function all work with strict. Forcing a specific strict tool = highest reliability.

Not in strict mode. Every property in your object must be required. Workaround: make the type a union with null, e.g. {"type": ["string", "null"]} and require the field.

No additional cost. Same input/output token pricing. The reliability improvement is free.