Fix Python JSON Errors: json.loads() Decode Errors and How to Repair Them
Python json.loads() raising JSONDecodeError? Learn every cause, what the error messages mean, and how to fix malformed JSON from APIs and AI tools.
Have broken JSON right now? Fix it free in under 1 second — no signup.
Fix My JSON →If you work with APIs or AI tools in Python, you've almost certainly hit a wall with json.loads() throwing a JSONDecodeError. The error message is often cryptic, the cause is rarely obvious, and the fix can take longer than it should. This guide covers every common cause, what each error message actually tells you, and how to handle malformed JSON defensively in production code.
What json.loads() Raises and Why
Python's built-in json module is strict by design. It implements RFC 8259 — the JSON specification — which means it rejects anything that isn't valid JSON: no trailing commas, no comments, no single quotes, no JavaScript-style values like undefined.
When json.loads() encounters invalid input, it raises json.JSONDecodeError, which is a subclass of ValueError. The exception carries three useful attributes:
import json
try:
data = json.loads('{"name": "Alice",}')
except json.JSONDecodeError as e:
print(e.msg) # Short message: "Expecting property name enclosed in double quotes"
print(e.lineno) # Line number where parsing failed
print(e.colno) # Column number
print(e.pos) # Character position in the string
print(e.doc) # The original input string
This is more information than most developers use. The pos attribute is especially useful: it pinpoints the exact character that broke parsing, not just the line.
The 7 Most Common JSONDecodeError Messages
1. Expecting value: line 1 column 1 (char 0)
What it means: The string is empty, or it starts with something that isn't a valid JSON value ({, [, ", a number, true, false, or null).
Typical causes:
- An API returned an empty body (HTTP 204 or an error page)
- You're parsing the raw
Responseobject instead of its.textor.json()content - The string is
None(passingNonetojson.loads()raisesTypeError, notJSONDecodeError)
import requests
response = requests.get("https://api.example.com/data")
Wrong — response is a Response object, not a string
json.loads(response)
Correct
data = response.json() # requests handles decoding
or
data = json.loads(response.text)
2. Expecting property name enclosed in double quotes: line N column N
What it means: The parser hit a } (end of object) but was expecting a key, OR it found a key that isn't wrapped in double quotes.
Trailing comma example:
{
"name": "Alice",
"age": 30,
}
Unquoted key example:
{name: "Alice", age: 30}
Both are common in AI-generated output. The JSON Fixer handles both automatically.
3. Expecting ':' delimiter: line N column N
What it means: A key was found but no colon followed it. Usually means a key-value pair is malformed.
{"name" "Alice"}
This sometimes appears in AI output when the model omits the colon between a long key and value.
4. Expecting ',' delimiter: line N column N
What it means: Two values appear side by side in an array or object without a comma separating them.
{"a": 1 "b": 2}
5. Extra data: line N column N (char N)
What it means: The JSON value ended (a complete {} or [] was found), but there's more non-whitespace text after it. This is the most common error when an API wraps JSON in a markdown code fence.
json
{"name": "Alice"}
The content before and after the JSON block — the triple backticks and language tag — cause this error.
6. Invalid \escape: line N column N
What it means: A string contains a backslash followed by a character that isn't a valid JSON escape sequence (", \, /, b, f, n, r, t, or uXXXX).
This appears frequently with Windows file paths embedded in JSON:
{"path": "C:\Users\Alice\Documents"}
The fix is to double-escape the backslash: C:\\Users\\Alice\\Documents.
7. Unterminated string starting at: line N column N
What it means: A string was opened with " but never closed. Common when parsing streaming or truncated AI output.
{"description": "This is a long text that got cut off mid-sentenc
Handling AI-Generated JSON in Python
AI tools like ChatGPT, Claude, and Gemini add several failure modes that a standard JSON file wouldn't have.
Python Booleans and None
Python uses True, False, and None. JSON requires true, false, and null. AI models trained on Python code sometimes produce the Python variants:
{"active": True, "deleted": False, "metadata": None}
json.loads() will fail on all three. A quick pre-processing step:
import re
def normalize_python_literals(s: str) -> str:
# Replace Python booleans and None outside of strings
# This is a simplified version — use aijsonmedic for full repair
s = re.sub(r'\bTrue\b', 'true', s)
s = re.sub(r'\bFalse\b', 'false', s)
s = re.sub(r'\bNone\b', 'null', s)
return s
raw = '{"active": True, "count": None}'
data = json.loads(normalize_python_literals(raw))
Note: a naive regex will incorrectly replace True inside a string value like "status": "TrueColor". The JSON Fixer uses a state-machine approach that skips string contents entirely. For a deeper guide including ast.literal_eval and how to prevent this at the model level, see Why LLMs Output True Instead of true.
Trailing Commas
The most common AI JSON error, especially in arrays:
raw = '[1, 2, 3,]'
Python's json module rejects this
json.loads(raw) -> JSONDecodeError
Quick fix for simple cases
import re
cleaned = re.sub(r',\s*([}\]])', r'\1', raw)
data = json.loads(cleaned)
Markdown-Wrapped JSON
When you ask an AI to output JSON, it often wraps it in a code fence:
def extract_json_from_markdown(text: str) -> str:
"""Strip markdown code fences from AI JSON output."""
# Match
json ... `` or ` ... pattern = r'
(?:json)?\s\n?([\s\S]?)\n?``'
match = re.search(pattern, text)
if match:
return match.group(1).strip()
return text.strip()
raw = '``json\n{"name": "Alice"}\n``'
clean = extract_json_from_markdown(raw)
data = json.loads(clean)
JavaScript-Style Comments
JSON does not allow comments. AI tools sometimes add them:
json
{
// User profile
"name": "Alice",
/ age field /
"age": 30
}
Stripping comments safely (without breaking strings that contain //) requires a state machine, not a simple regex. Use the JSON Fixer for this case.
Defensive Coding Pattern: Validate Before Parse
For production code that receives JSON from external sources, this pattern is robust:
python
import json
from typing import Any
def safe_parse(raw: str | bytes, *, fallback: Any = None) -> Any:
"""
Parse JSON with graceful fallback.
Returns parsed data on success, fallback value on failure.
Logs the error for observability.
"""
if not raw:
return fallback
if isinstance(raw, bytes):
try:
raw = raw.decode("utf-8")
except UnicodeDecodeError:
raw = raw.decode("latin-1")
raw = raw.strip()
# Fast path — valid JSON
try:
return json.loads(raw)
except json.JSONDecodeError as e:
# Log the specific error for debugging
import logging
logging.warning(
"JSON parse failed at pos %d (line %d col %d): %s",
e.pos, e.lineno, e.colno, e.msg
)
return fallback
For an automated repair step before falling back, paste the raw output into the JSON Fixer to identify exactly what's wrong and get a clean version.
Using the json Module's Error Position
The pos attribute lets you show the user (or yourself) exactly where parsing failed:
python
def parse_with_context(raw: str, context_chars: int = 40) -> dict:
try:
return json.loads(raw)
except json.JSONDecodeError as e:
start = max(0, e.pos - context_chars)
end = min(len(raw), e.pos + context_chars)
snippet = raw[start:end]
pointer = " " * (e.pos - start) + "^"
raise ValueError(
f"JSON error at position {e.pos}: {e.msg}\n"
f" ...{snippet}...\n"
f" {pointer}"
) from e
Python 2 vs Python 3
If you're still maintaining Python 2 code (end-of-life since 2020, but legacy systems persist):
Feature Python 2 Python 3 Module import jsonimport jsonException ValueErrorjson.JSONDecodeError (subclass of ValueError)Unicode strings json.loads(u"...") requiredstr is Unicode by defaultBytes input Accepted in some cases Use .decode() first
In Python 3, catch json.JSONDecodeError specifically — it gives you lineno, colno, and pos. In Python 2, you only get ValueError with a message string.
Validating JSON Structure After Parsing
Parsing successfully doesn't mean the data is what you expect. Use type checks or a validation library:
python
from typing import TypedDict
class UserProfile(TypedDict):
name: str
age: int
email: str
def parse_user(raw: str) -> UserProfile:
data = json.loads(raw)
if not isinstance(data, dict):
raise ValueError(f"Expected object, got {type(data).__name__}")
required = {"name", "age", "email"}
missing = required - data.keys()
if missing:
raise ValueError(f"Missing required fields: {missing}")
return data # type: ignore[return-value]
For more complex schemas, consider pydantic (v2 is especially fast) or jsonschema. The JSON Validator can check your JSON against a schema interactively before you write validation code.
Quick Reference: Common Fixes
Error message Likely cause Fix Expecting value (char 0)Empty response Check API response status Expecting property nameTrailing comma or unquoted key Strip trailing commas Expecting ':'Missing colon in object Check AI output manually Extra dataMarkdown fence wrapping Strip before parsing Unterminated stringTruncated streaming output Detect and repair truncation Invalid \escapeWindows path in string Double-escape backslashes
For any JSON that's too complex to fix with a one-liner regex, use the JSON Fixer — it runs 14 repair stages including comment stripping, Python literal conversion, truncation recovery, and more.
Summary
Python's json.loads() is strict for good reason, but real-world JSON — especially from AI tools — is rarely clean. The key practices are:
- Always wrap
json.loads() in a try/except json.JSONDecodeError block - Check
e.pos, e.lineno, and e.colno to locate the exact failure point - Pre-process AI output: strip markdown fences, normalize Python literals
- Use the JSON Validator to diagnose structure problems interactively
- Use the JSON Formatter to pretty-print before debugging
When a piece of JSON is genuinely broken and you need to understand why, paste it into the JSON Fixer for a full repair report that shows every issue found and every change made. For a complete breakdown of every error message and what they mean, see JSON Parse Error — What It Means and How to Fix It Fast.
FAQ
What is the most common Python JSON error and how do I fix it?
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0) — this means the input is empty. Check your response before parsing: if not response.text: raise ValueError("Empty response"). The second most common is Expecting ',' delimiter which usually means a trailing comma or Python literal (True/False/None) was in the JSON.
Why does json.loads fail on AI-generated output in Python?
AI models frequently produce Python-style output that looks like JSON but isn't: True/False/None instead of true/false/null, single-quoted strings, and unquoted keys. Use ast.literal_eval() for pure Python dict output, or run through a repair library first: from json_repair import repair_json; data = json.loads(repair_json(raw)).
How do I handle the JSONDecodeError with a line and column number?
The error message includes the position: JSONDecodeError: Unexpected character ',' at line 3, column 25. Count to that character in the raw string to find the issue. In code: e.lineno, e.colno, and e.doc (the original string) are available as attributes on the exception — log them for debugging.
What is the difference between json.load() and json.loads() in Python?
json.load(file) reads from a file-like object (pass an open file handle). json.loads(string) parses a string. The s stands for "string." The most common mistake is passing a file path string to json.loads() — it tries to parse the path itself as JSON and fails immediately.
How do I write Python data to a JSON file safely?
Use json.dump() with ensure_ascii=False to preserve unicode characters, and indent=2 for readable output: json.dump(data, f, ensure_ascii=False, indent=2). Always open the file with encoding='utf-8' to avoid encoding errors on Windows. Use default=str as a fallback serializer for types that aren't JSON-serializable (like datetime objects).
Still dealing with broken JSON?
Paste it in and get it fixed in under 1 second — free, no signup, no install. Works with ChatGPT, Claude, n8n, and any AI output.
Fix My JSON Free →Related Articles