Bad control character in string literal: konec řádku v řetězci JSON

V řetězci JSON nesmí být přímo znaky U+0000 až U+001F, tedy ani konec řádku nebo tabulátor; zapisují se jako , nebo (RFC 8259, oddíl 7). Node.js a Chrome hlásí Bad control character in string literal, Python Invalid control character, PHP Control character error, possibly incorrectly encoded.

Autor: Aktualizováno

Znění hlášky v jednotlivých prostředích

Parser Hláška Kdy
Node.js 25, Chrome 153 (V8) JSON.parse, response.json(), Edge a další prohlížeče s V8 Bad control character in string literal in JSON at position 14 (line 1 column 15) Konec řádku přímo v řetězci
Bad control character in string literal in JSON at position 7 (line 1 column 8) Tabulátor v řetězci
Python 3.14 json.loads, json.load (json.JSONDecodeError) Invalid control character at: line 1 column 15 (char 14) Konec řádku přímo v řetězci
Invalid control character at: line 1 column 8 (char 7) Tabulátor v řetězci
Python 3.9 starší verze, např. systémový Python v macOS Invalid control character at: line 1 column 15 (char 14) Konec řádku přímo v řetězci
Invalid control character at: line 1 column 8 (char 7) Tabulátor v řetězci
.NET 10 (System.Text.Json) JsonDocument.Parse, JsonSerializer.Deserialize '0x0A' is invalid within a JSON string. The string should be correctly escaped. Konec řádku přímo v řetězci
'0x09' is invalid within a JSON string. The string should be correctly escaped. Tabulátor v řetězci
PHP 8.4 json_last_error_msg(), JsonException s JSON_THROW_ON_ERROR Control character error, possibly incorrectly encoded Konec řádku přímo v řetězci, Tabulátor v řetězci

Hláška PHP mluví o kódování, ale chyba JSON_ERROR_CTRL_CHAR znamená řídicí znak v řetězci. S kódováním souvisí jen tehdy, když se text přečetl ve špatném kódování a bajt se omylem stal řídicím znakem.

Příčiny a opravy

Konec řádku přímo v řetězci

Chybně
{"text":"první
řádek"}
Opraveno
{"text":"první\nřádek"}

Víceřádkový text (popis, adresa, poznámka z formuláře) vložený do JSON spojováním řetězců nese skutečný konec řádku. JSON ho musí mít jako dvojici znaků \n. Mezi položkami a kolem závorek konce řádků vadit nemusí, jde jen o obsah řetězce.

Tabulátor v řetězci

Chybně
{"a":"x	y"}
Opraveno
{"a":"x\ty"}

Tabulátor nepoznáš okem: v editoru vypadá jako mezery. Stává se to u textu zkopírovaného z tabulky nebo z výstupu, který odděloval sloupce tabulátorem.

Spolehlivou opravou je vytvářet JSON serializátorem. Ten řídicí znaky escapuje sám:

Serializátor escapuje konec řádku i tabulátor
JSON.stringify({ text: 'první\nřádek\tx' })
// {"text":"první\nřádek\tx"}

json.dumps({'text': 'první\nřádek\tx'}, ensure_ascii=False)
# {"text": "první\nřádek\tx"}

Když JSON přichází od někoho jiného

Text s řídicími znaky není platný JSON a oprava patří ke zdroji. Python má pro přechodné období strict=False, které řídicí znaky v řetězcích přijme. Jiné parsery, třeba JSON.parse, takovou volbu nemají.

Python: tolerantní čtení
import json

# Jen pro data, která nejdou opravit u zdroje: přijme řídicí znaky v řetězcích.
json.loads('{"text":"první\nřádek"}', strict=False)

Nahrazovat konce řádků v celém textu nejde: smazal bys i odsazení mezi položkami, které je v pořádku, a změnil obsah. Pravidla zápisu znaků v řetězcích popisuje kapitola Řetězce v JSON.