Skip to content

How to Resolve Python "JSONDecodeError: Invalid control character at ..."

Fix the Python JSONDecodeError for unescaped control characters (like or ) in JSON strings by using strict=False in json.loads() or pre-processing the input with escaping or removal.

When parsing JSON data in Python using the json module, you might encounter the json.decoder.JSONDecodeError: Invalid control character at .... This specific error indicates that the JSON parser found an unescaped "control character" (like a newline \n or tab \t) within a string value, which is disallowed by default according to strict JSON standards.

This guide explains why this error occurs and provides the primary solution using the strict parameter, along with alternative approaches.

Understanding the Error: Control Characters in JSON

Control characters are special, often non-printing characters within the ASCII range 0-31. Common examples include newline (\n), tab (\t), carriage return (\r), and null (\0). Standard JSON syntax requires these characters to be escaped with a backslash if they appear inside a string value (e.g., "line1\\nline2" is valid JSON, but "line1\nline2" is not strictly valid).

By default, Python's json.loads() and json.load() functions operate in a strict mode (strict=True) that adheres to this standard and disallows unescaped control characters within strings, leading to the error.

Cause: Unescaped Control Characters with strict=True

The error occurs when you pass a string containing raw, unescaped control characters to json.loads() (or read such content using json.load()) without telling the parser to relax its rules.

import json

invalid_json_string = '{"message": "Hello\nWorld"}'

print(f"Input string (repr): {repr(invalid_json_string)}")

try:
  data = json.loads(invalid_json_string) # strict=True by default
  print(data)
except json.JSONDecodeError as e:
  print(e)

Output:

Input string (repr): '{"message": "Hello\nWorld"}'
Invalid control character at: line 1 column 19 (char 18)

The parser encounters the raw \n at character index 19 and raises the error because it expects control characters to be escaped (like \\n) in strict mode.

The json module provides a direct way to handle this specific error: set the strict parameter to False when calling json.loads() or json.load(). This tells the parser to permit unescaped control characters within strings.

import json

json_string_with_newline = '{"message": "Hello\nWorld"}'

print(f"Input string (repr): {repr(json_string_with_newline)}")

try:
  data = json.loads(json_string_with_newline, strict=False)

  print("\nParsed data:", data)
  print(f"Value contains newline: {'\n' in data['message']}") # Output: True

except json.JSONDecodeError as e:
    print(f"Error despite strict=False: {e}")

Output:

Input string (repr): '{"message": "Hello\nWorld"}'

Parsed data: {'message': 'Hello\nWorld'}
Value contains newline: True

Setting strict=False is the intended mechanism within the json library for parsing JSON-like data that might contain raw control characters.

Solution 2: Remove Control Characters Before Parsing

If you don't need the control characters in your final data, you can remove them from the string before parsing. This approach modifies the data.

import json

json_string_with_newline = '{"message": "Hello\nWorld", "note": "Tab\there"}'
print(f"Original string (repr): {repr(json_string_with_newline)}")

cleaned_string = json_string_with_newline.replace('\n', '').replace('\t', '')
print(f"Cleaned string: {cleaned_string}")

try:
  data = json.loads(cleaned_string)
  print("Parsed data after removal:", data)
except json.JSONDecodeError as e:
  print(f"Error parsing cleaned string: {e}")

Output:

Original string (repr): '{"message": "Hello\nWorld", "note": "Tab\there"}'
Cleaned string: {"message": "HelloWorld", "note": "Tabhere"}
Parsed data after removal: {'message': 'HelloWorld', 'note': 'Tabhere'}

Solution 3: Escape Control Characters Before Parsing

If you need to preserve the meaning of the control character but make the JSON string strictly valid, you can escape them by adding an extra backslash before parsing.

import json

json_string_with_newline = '{"message": "Hello\nWorld"}'
print(f"Original string (repr): {repr(json_string_with_newline)}")

escaped_string = json_string_with_newline.replace('\n', '\\n') # Replace \n with \\n
print(f"Escaped string (repr): {repr(escaped_string)}")

try:
  data = json.loads(escaped_string)
  print("Parsed data after escaping:", data)
  print(f"Value contains newline: {'\n' in data['message']}") 
except json.JSONDecodeError as e:
  print(f"Error parsing escaped string: {e}")

Output:

Original string (repr): '{"message": "Hello\nWorld"}'
Escaped string (repr): '{"message": "Hello\\nWorld"}'
Parsed data after escaping: {'message': 'Hello\nWorld'}
Value contains newline: True

Replacing \n with \\n, \t with \\t, etc., creates a string that conforms to strict JSON syntax rules regarding control characters.

Handling Files (json.load() with strict=False)

The same strict=False parameter can be used with json.load() when reading from a file object that might contain unescaped control characters.

import json
import io # For simulating a file

file_content = '{"config": "path/to/file\nline2"}'
file_simulation = io.StringIO(file_content)

try:
  data = json.load(file_simulation, strict=False)
  print("Parsed data from file stream:", data)
except json.JSONDecodeError as e:
  print(f"Error loading from file stream: {e}")

file_simulation_strict = io.StringIO(file_content)
try:
    data_strict = json.load(file_simulation_strict) # Default strict=True
except json.JSONDecodeError as e:
    print(f"\nError loading from file stream (strict=True): {e}")

Output:

Parsed data from file stream: {'config': 'path/to/file\nline2'}

Error loading from file stream (strict=True): Invalid control character at: line 1 column 25 (char 24)

Alternatively, you could read the entire file content into a string (f.read()), perform removal or escaping (Solutions 2 or 3), and then parse the modified string using json.loads().

While the "Invalid control character" error is specific, sometimes users encounter other JSONDecodeError types when dealing with malformed strings. A common one is Unterminated string starting at..., which usually means a closing double quote (") is missing for a key or string value.

import json

malformed_string = '{"name": "Tom Nolan}'

try:
    data = json.loads(malformed_string)
except json.JSONDecodeError as e:
    print(e)

Output:

Unterminated string starting at: line 1 column 10 (char 9)

Solution: Ensure all strings and keys in your JSON data are properly enclosed in double quotes ("). This is a different issue than control characters but also relates to JSON validity.

Conclusion

The JSONDecodeError: Invalid control character at ... specifically occurs when Python's json parser, in its default strict mode (strict=True), encounters unescaped control characters (like \n, \t) within JSON strings.

The most direct solution is to allow these characters by setting strict=False in your json.loads() or json.load() call. This is the intended way if you need to parse JSON-like data that includes raw control characters.

Alternatively, if you don't need the control characters or want to ensure strict JSON validity before parsing, you can remove them (e.g., string.replace('\n', '')) or escape them (e.g., string.replace('\n', '\\n')) in the input string prior to parsing. Remember to also ensure overall JSON validity (correct quoting, no trailing commas, etc.) to avoid other types of JSONDecodeError.

Navigation