The --json flag (both inline and @filename) rejects perfectly valid JSON whenever a string value decodes to a character including U+000A, even when the JSON source uses the spec-compliant \n escape.
Repro
# Build a valid JSON file using Python's json.dumps (newline escaped as \n)
python3 -c 'import json; open("/tmp/escaped.json","w").write(json.dumps({"id":"x","name":"x","type":"COMPONENT","settings":{},"parts":{"html":"line1\nline2"}}))'
# Python parses it without issue — confirming the JSON is well-formed
python3 -c 'import json; print(json.load(open("/tmp/escaped.json")))'
# → {'id': 'x', ..., 'parts': {'html': 'line1\nline2'}}
# cio rejects it
cio api /v1/environments/216574/ds/nodes -X POST --json @/tmp/escaped.json --dry-run
# → {"error":true,"code":"VALIDATION_ERROR","message":"JSON string value contains control character at position 5 (U+000A) (from file /tmp/escaped.json)","details":{"flag":"--json"}}
Per RFC 8259 §7, \n inside a string is the correct way to encode a literal newline. The CLI is validating the decoded string value for control chars rather than the JSON source, which makes it impossible to pass HTML, Carta markup, or any other multi-line content via --json without first collapsing newlines to spaces.
Expected
Treat input as valid JSON whenever it parses as valid JSON. If there's a security/safety concern with raw control chars hitting downstream APIs, surface it as a warning or add an explicit opt-in flag (--trust-json or --no-control-char-check), but don't break valid JSON by default.
Workaround (current)
Collapse HTML / multi-line content to a single line before passing to --json. This is what the in-app and Design Studio workflows have to do today.
Confirmed on @customerio/cli@0.0.5 (latest as of 2026-05-14).
The
--jsonflag (both inline and@filename) rejects perfectly valid JSON whenever a string value decodes to a character includingU+000A, even when the JSON source uses the spec-compliant\nescape.Repro
Per RFC 8259 §7,
\ninside a string is the correct way to encode a literal newline. The CLI is validating the decoded string value for control chars rather than the JSON source, which makes it impossible to pass HTML, Carta markup, or any other multi-line content via--jsonwithout first collapsing newlines to spaces.Expected
Treat input as valid JSON whenever it parses as valid JSON. If there's a security/safety concern with raw control chars hitting downstream APIs, surface it as a warning or add an explicit opt-in flag (
--trust-jsonor--no-control-char-check), but don't break valid JSON by default.Workaround (current)
Collapse HTML / multi-line content to a single line before passing to
--json. This is what the in-app and Design Studio workflows have to do today.Confirmed on
@customerio/cli@0.0.5(latest as of 2026-05-14).