# How to Fix Python json.JSONDecodeError
The json.JSONDecodeError occurs when Python's json module fails to parse a string as valid JSON. This error provides detailed information about where and why the parsing failed, making it easier to diagnose malformed JSON data.
Error Patterns
Expecting Value
Traceback (most recent call last):
File "app.py", line 5, in <module>
json.loads('{"key": }')
json.JSONDecodeError: Expecting value: line 1 column 9 (char 8)Expecting Property Name
Traceback (most recent call last):
File "app.py", line 10, in <module>
json.loads('{1: "value"}')
json.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)Extra Data
Traceback (most recent call last):
File "app.py", line 15, in <module>
json.loads('{"key": "value"}{"key2": "value2"}')
json.JSONDecodeError: Extra data: line 1 column 18 (char 17)Invalid Control Character
Traceback (most recent call last):
File "app.py", line 20, in <module>
json.loads('"text\nwith\nnewlines"')
json.JSONDecodeError: Invalid control character at: line 1 column 6 (char 5)Unterminated String
Traceback (most recent call last):
File "app.py", line 25, in <module>
json.loads('"unterminated string')
json.JSONDecodeError: Unterminated string starting at: line 1 column 1 (char 0)Common Causes
- 1.Trailing commas - JSON doesn't allow trailing commas like Python
- 2.Single quotes - JSON requires double quotes for strings
- 3.Missing values - Key without value in object
- 4.Unquoted keys - Object keys must be in double quotes
- 5.Extra data - Multiple JSON objects in single string
- 6.Control characters - Unescaped newlines, tabs in strings
- 7.Invalid encoding - Non-UTF-8 encoding
- 8.Missing closing brackets - Unterminated objects or arrays
Diagnosis Steps
Step 1: Check Error Position
```python import json
try: data = json.loads(json_string) except json.JSONDecodeError as e: print(f"Error: {e.msg}") print(f"Line: {e.lineno}") print(f"Column: {e.colno}") print(f"Position (char): {e.pos}")
# Show context around error start = max(0, e.pos - 20) end = min(len(json_string), e.pos + 20) print(f"Context: ...{json_string[start:end]}...") print(f" {' ' * (min(20, e.pos - start) + 3)}^ Error here") ```
Step 2: Validate JSON Structure
```python import json
def diagnose_json(json_string): """Diagnose common JSON issues.""" print(f"String length: {len(json_string)}") print(f"First 50 chars: {json_string[:50]}") print(f"Last 50 chars: {json_string[-50:]}")
# Check for common issues issues = []
# Single quotes if "'" in json_string and '"' not in json_string: issues.append("Uses single quotes instead of double quotes")
# Trailing comma if json_string.strip().endswith(','): issues.append("Possible trailing comma")
# Unbalanced brackets open_braces = json_string.count('{') close_braces = json_string.count('}') if open_braces != close_braces: issues.append(f"Unbalanced braces: {open_braces} open, {close_braces} close")
open_brackets = json_string.count('[') close_brackets = json_string.count(']') if open_brackets != close_brackets: issues.append(f"Unbalanced brackets: {open_brackets} open, {close_brackets} close")
for issue in issues: print(f"Potential issue: {issue}")
return issues ```
Step 3: Use JSON Validator
```bash # Online validators # https://jsonlint.com # https://jsonformatter.curiousconcept.com
# Command line python -m json.tool invalid.json # Shows parse error jq . invalid.json # Alternative validator ```
```python # Python validation import json
def validate_json_file(filepath): """Validate JSON file.""" with open(filepath) as f: content = f.read()
try: json.loads(content) print("JSON is valid") return True except json.JSONDecodeError as e: print(f"Invalid JSON: {e}") return False ```
Solutions
Solution 1: Fix Trailing Commas
```python import json
# Problem: Trailing comma json_string = '{"items": [1, 2, 3,]}' json.loads(json_string) # JSONDecodeError
# Fix: Remove trailing comma json_string = '{"items": [1, 2, 3]}' data = json.loads(json_string) # Works
# Or fix programmatically def remove_trailing_commas(json_string): """Remove trailing commas from JSON string.""" import re # Remove trailing comma before ] or } fixed = re.sub(r',\s*([}]])', r'\1', json_string) return fixed
# Apply fix fixed_string = remove_trailing_commas(json_string) data = json.loads(fixed_string) ```
Solution 2: Fix Single Quotes to Double Quotes
```python import json
# Problem: Single quotes json_string = "{'key': 'value'}" json.loads(json_string) # JSONDecodeError
# Fix: Use double quotes json_string = '{"key": "value"}' data = json.loads(json_string) # Works
# Or convert programmatically def convert_quotes(json_string): """Convert single quotes to double quotes.""" # This is tricky - need to handle escaped quotes import re
# Replace single quotes around keys and values # Be careful not to replace quotes inside strings fixed = json_string.replace("'", '"')
# This simple approach may fail for complex cases # Better: use ast.literal_eval for Python-style dict return fixed
# Alternative: Use ast for Python-style strings import ast python_dict = ast.literal_eval("{'key': 'value'}") # Then convert to JSON properly json_string = json.dumps(python_dict) ```
Solution 3: Fix Missing Values
```python import json
# Problem: Key without value json_string = '{"key": }' json.loads(json_string) # JSONDecodeError
# Fix: Add value json_string = '{"key": null}' # Use null for missing value data = json.loads(json_string)
# Or empty string json_string = '{"key": ""}' data = json.loads(json_string) ```
Solution 4: Handle Extra Data
```python import json
# Problem: Multiple JSON objects json_string = '{"a": 1}{"b": 2}' json.loads(json_string) # JSONDecodeError: Extra data
# Fix: Use JSONDecoder with raw_decode decoder = json.JSONDecoder() data, end = decoder.raw_decode(json_string) print(data) # {"a": 1} remaining = json_string[end:] print(remaining) # {"b": 2}
# Parse multiple objects def parse_multiple_json(json_string): """Parse multiple JSON objects from string.""" decoder = json.JSONDecoder() objects = [] idx = 0
while idx < len(json_string): # Skip whitespace while idx < len(json_string) and json_string[idx].isspace(): idx += 1
if idx >= len(json_string): break
obj, end = decoder.raw_decode(json_string[idx:]) objects.append(obj) idx += end
return objects
objects = parse_multiple_json('{"a": 1}{"b": 2}{"c": 3}') print(objects) # [{'a': 1}, {'b': 2}, {'c': 3}] ```
Solution 5: Fix Control Characters
```python import json
# Problem: Unescaped control characters json_string = '{"text": "line1\nline2"}' json.loads(json_string) # JSONDecodeError: Invalid control character
# Fix: Escape control characters json_string = '{"text": "line1\\nline2"}' data = json.loads(json_string) # Works print(data['text']) # "line1\nline2" (actual newline in value)
# Or when creating JSON, let json.dumps handle it data = {"text": "line1\nline2"} json_string = json.dumps(data) # Properly escapes print(json_string) # {"text": "line1\nline2"}
# Manual escape for common characters def escape_json_string(s): """Escape string for JSON.""" escape_map = { '\n': '\\n', '\r': '\\r', '\t': '\\t', '\b': '\\b', '\f': '\\f', '"': '\\"', '\\': '\\\\', } for char, escaped in escape_map.items(): s = s.replace(char, escaped) return s ```
Solution 6: Handle Encoding Issues
```python import json
# Problem: Non-UTF-8 encoding json_string = '{"key": "value"}'.encode('latin-1') json.loads(json_string) # May fail or decode incorrectly
# Fix: Ensure UTF-8 encoding json_string = '{"key": "value"}'.encode('utf-8') data = json.loads(json_string) # Works
# Or decode first json_bytes = b'{"key": "value"}' json_string = json_bytes.decode('utf-8') data = json.loads(json_string)
# Handle mixed encoding def safe_json_loads(content): """Load JSON with encoding handling.""" if isinstance(content, bytes): # Try UTF-8 first try: content = content.decode('utf-8') except UnicodeDecodeError: # Try other encodings content = content.decode('utf-8', errors='replace')
return json.loads(content) ```
Solution 7: Fix File Reading Issues
```python import json
# Problem: Reading multiple JSON objects from file with open('data.json') as f: data = json.load(f) # Fails if file has multiple objects
# Fix: Read line by line if each line is JSON def read_json_lines(filepath): """Read JSON objects from file (one per line).""" objects = [] with open(filepath) as f: for line in f: line = line.strip() if line: objects.append(json.loads(line)) return objects
# Fix: Use raw_decode for concatenated JSON def read_concatenated_json(filepath): """Read multiple concatenated JSON objects.""" with open(filepath) as f: content = f.read()
return parse_multiple_json(content) ```
Solution 8: Robust JSON Parsing
```python import json import re
def robust_json_parse(json_string, fix_trailing_commas=True, fix_quotes=False): """Parse JSON with automatic fixes.""" original = json_string
if fix_trailing_commas: json_string = re.sub(r',\s*([}]])', r'\1', json_string)
if fix_quotes: # Be careful with this - can break valid JSON pass
try: return json.loads(json_string) except json.JSONDecodeError as e: # Try to provide helpful message print(f"Could not fix JSON automatically") print(f"Original error: {e.msg} at position {e.pos}")
# Show problematic area start = max(0, e.pos - 30) end = min(len(original), e.pos + 30) print(f"Context: ...{original[start:end]}...")
raise
# Usage try: data = robust_json_parse('{"items": [1, 2, 3,]}') print(data) # {'items': [1, 2, 3]} except json.JSONDecodeError: # Handle manually pass ```
JSON Validation Patterns
Pre-validate JSON
```python import json
def safe_json_parse(content, default=None): """Parse JSON safely with default fallback.""" try: return json.loads(content) except (json.JSONDecodeError, TypeError) as e: print(f"JSON parse error: {e}") return default
# Usage data = safe_json_parse(api_response, default={}) if data is None: print("Failed to parse JSON, using empty object") data = {} ```
Validate JSON Schema
```python import json try: from jsonschema import validate except ImportError: pass # jsonschema not installed
def validate_with_schema(json_data, schema): """Validate JSON against schema.""" try: validate(instance=json_data, schema=schema) return True except Exception as e: print(f"Schema validation error: {e}") return False
# Schema example user_schema = { "type": "object", "required": ["name", "email"], "properties": { "name": {"type": "string"}, "email": {"type": "string", "format": "email"}, "age": {"type": "integer", "minimum": 0} } }
data = {"name": "Alice", "email": "alice@example.com"} if validate_with_schema(data, user_schema): print("Valid user data") ```
Fix API Response JSON
```python import json import requests
def safe_api_request(url): """Request API with JSON error handling.""" response = requests.get(url)
# Check response content content = response.text
# Pre-process content = content.strip()
# Try parsing try: return json.loads(content) except json.JSONDecodeError as e: print(f"API returned invalid JSON") print(f"Status code: {response.status_code}") print(f"Content preview: {content[:100]}...") print(f"Parse error: {e}")
# Maybe it's not JSON at all if content.startswith('<'): print("Response appears to be HTML, not JSON")
return None ```
Common JSON Mistakes
```python # Common mistakes and fixes
# 1. Trailing comma # WRONG: {"items": [1, 2, 3,]} # RIGHT: {"items": [1, 2, 3]}
# 2. Single quotes # WRONG: {'key': 'value'} # RIGHT: {"key": "value"}
# 3. Unquoted keys # WRONG: {key: "value"} # RIGHT: {"key": "value"}
# 4. Comments (JSON doesn't support comments) # WRONG: {"key": "value" /* comment */} # RIGHT: {"key": "value"}
# 5. Undefined/None should be null # WRONG: {"value": undefined} # RIGHT: {"value": null}
# 6. Missing closing brackets # WRONG: {"key": "value" # RIGHT: {"key": "value"}
# 7. Numbers without quotes are fine # RIGHT: {"count": 42, "price": 19.99}
# 8. Booleans without quotes # RIGHT: {"active": true, "deleted": false} ```
Prevention Tips
- 1.Use json.dumps() for creating JSON - handles escaping properly
- 2.Validate input before parsing
- 3.Handle errors gracefully with defaults
- 4.Check encoding when reading from files/network
- 5.Use schema validation for critical data
```python # Good pattern: Safe JSON handling import json
def process_json_input(content): """Process JSON input safely.""" # Ensure string if isinstance(content, bytes): content = content.decode('utf-8')
# Strip whitespace content = content.strip()
# Validate structure hints if not content: return {}
if content[0] not in '{[': raise ValueError("JSON must start with { or [")
# Parse try: return json.loads(content) except json.JSONDecodeError as e: # Log error with context print(f"JSON error at position {e.pos}: {e.msg}") raise ValueError(f"Invalid JSON: {e.msg}") ```
Related Errors
TypeError- Input not string or bytesValueError- Similar errors in older Python versionsUnicodeDecodeError- Encoding issues before parsingFileNotFoundError- When loading from missing file