YAML Multi-line Strings: The Real Difference Between "", '', >, and |
A practical, spec-backed guide to YAML multi-line strings, including folding, chomping (-/+), quoting rules, and production-safe patterns.
The incident was tiny and expensive: a CI pipeline looked fine in review, then failed in production because one YAML value silently collapsed line breaks into spaces. No syntax error, no warning, just different runtime data than expected.
That is why YAML multi-line strings deserve more than a quick cheat sheet. The style you pick ("...", '...', >, |) controls folding, escaping, and final newlines. This guide is based on YAML 1.2.2 behavior and focuses on practical choices for real configs.
Mental model first: flow scalars vs block scalars
YAML strings are easier to reason about when grouped into two families:
- Flow scalars:
"...",'...', and plain (unquoted) strings - Block scalars:
>(folded) and|(literal), optionally with chomping indicators like-and+
YAML 1.2.2 states that flow scalars can span multiple lines and line breaks are folded. For block scalars, folding behavior depends on whether you chose > or |, and how you configured chomping.
Quoted strings: "..." vs '...'
Double-quoted strings
- Supports escape sequences such as
\n,\t, and Unicode escapes. - You must escape
"and\. - Useful when you need explicit control characters in the resulting value.
Single-quoted strings
- No backslash escapes are interpreted.
\nremains two characters. - A single quote is escaped by doubling it:
''. - Backslashes are treated literally, which is great for Windows paths or regex fragments.
double: "line1\nline2"
single: 'line1\nline2'
quote: 'It''s safe here'
# Parsed results:
# double => line1 + newline + line2
# single => line1\nline2
# quote => It's safe here Block styles: >, |, and chomping
This is where most production bugs come from.
>(folded): normal line breaks become spaces.|(literal): line breaks are preserved.-(strip): removes final newline and trailing empty lines.+(keep): keeps final newline and trailing empty lines.- No chomping indicator means clip (default): keep the final newline, drop extra trailing empty lines.
folded: >
one
two
literal: |
one
two
strip: |-
one
two
keep: |+
one
two
# Parsed (conceptual):
# folded => "one two\n"
# literal => "one\ntwo\n"
# strip => "one\ntwo"
# keep => "one\ntwo\n" (plus preserved trailing empties) One subtle but important rule from the spec: folded style does not fold everything. Blank lines and more-indented lines are preserved as line breaks, which is why lists or indented snippets inside a folded block can still keep structure.
Quick behavior matrix
Use this as a fast recall table (␠ = space, ⏎ = newline):
| Case | > | | | >- | |- | "..." | '...' |
|---|---|---|---|---|---|---|
| Single line break | ␠ | ⏎ | ␠ | ⏎ | ␠ (flow fold) | ␠ (flow fold) |
| Blank line between text lines | ⏎ | ⏎⏎ | ⏎ | ⏎⏎ | typically ⏎ | typically ⏎ |
| Final newline | kept (clip) | kept (clip) | removed | removed | context-dependent | context-dependent |
| Escape sequence support | No | No | No | No | Yes | No |
Production examples that avoid surprises
1) CI scripts in GitHub Actions
If line breaks are meaningful (shell scripts, SQL blocks), use | or |-.
env:
DEPLOY_SCRIPT: |-
echo "build start"
npm run build
npm run preview 2) Human-readable long messages
If you want readable source but mostly one wrapped line at runtime (release notes, annotations), use >-.
message: >-
Deploy completed for service A.
Health checks passed.
No migrations were required. 3) ConfigMap content in Kubernetes
For embedded file content, default to |. If your app fails because of one extra terminal newline, switch to |-.
Practical selection checklist
- Need exact line breaks? Choose
|. - Need wrapped prose that becomes one logical line? Choose
>. - Need to remove terminal newline? Add
-. - Need to preserve trailing empty lines? Add
+. - Need escape sequences like
\ninside value? Use double quotes. - Need literal backslashes and minimal escaping? Use single quotes.
"Most YAML multi-line bugs are not parser bugs. They are style-choice bugs."
References and final takeaway
For exact behavior, trust the YAML spec over memory. The key sections are flow scalar styles and block scalar styles with chomping rules. The community comparison thread below is also useful for quick pattern checks when you're debugging edge cases.
- YAML 1.2.2 Specification (see sections 2.3, 7.3, 8.1)
- Stack Overflow: multi-line YAML string patterns
If you want a quick pre-check before shipping config edits, run your snippet through a parser and compare the rendered value, then sanity-check length and line count with Text Counter. For writing constraints in docs and prompts, this pomodoro deep-dive pairs nicely with YAML workflows.