Problem
Six defects in the YAML formatter, all reproducible on feat/3.0 and all found while working on #634. None are caused by that work — I verified each against a pristine checkout — and none are fixed by the PR for it. Filing them together because they were found by one effort and cluster in the same area; happy to split into one issue per defect to match #632–#635 if you'd prefer.
Four surfaced through differential fuzzing (pristine feat/3.0 compiled alongside a patched copy, asserting no data is lost that the baseline preserved). Two came from prettier comparison.
Ordered by severity as I read it.
1. Output fails to parse — flow tag loses its separating space
in: "A: [0000,\n00,!00 ]\n"
out: "A: [0000,\n00,!00]\n"
The space after the !00 tag is stripped as trailing whitespace, and the result is no longer valid YAML:
yaml: line 2: did not find expected ',' or ']'
Worst of the six: the formatter emits a file the formatter itself will then reject.
2. Idempotency break — --- inside a plain scalar gets indented
in: "-:\n- 0\n---\"0:\n"
pass1: "-:\n - 0\n ---\"0:\n"
pass2: error — yaml: line 1: did not find expected '-' indicator
A --- that is scalar content is treated as a document marker and reindented, so the second format pass fails. Found by the repo's own FuzzYAMLFormatter.
3. Nested sequence loses a level
in: "- \n - 00\n"
out: "-\n- 00\n"
[[0]] becomes [null, 0] — the nesting is flattened and a null appears.
4. Explicit complex keys collapse into a duplicate-key collision
in: "? 0\n\n\n 1\n? 0\n\n 1\n? 0\n\n\n !"
out: "? 0\n\n 1\n? 0\n\n 1\n? 0\n\n !\n"
Every blank run is collapsed, so two keys that differ only by a blank line become identical, and the output fails to parse:
yaml: unmarshal errors: mapping key "0\n1" already defined at line 1
The blank lines are content here — YAML folds them into newlines in the key. Partially improved by the #634 PR (the output becomes parseable), not fully fixed.
5. Block scalar gains a trailing newline
in: "A: |\n 0" (no final newline)
out: "A: |\n 0\n"
value before: "0"
value after: "0\n"
Note this only shows up when the input has no final newline, so comparing the input's semantics against the output's requires care not to normalize the difference away.
6. Unterminated flow read as a plain-scalar continuation loses a blank line
in: "a: foo\n\n\n {bar\n"
out: "a: foo\n\n { bar\n"
value before: "foo\n\n{bar"
value after: "foo\n{ bar"
The tokenizer emits TokFlow for the unterminated {, but yaml.v3 parses it as a continuation of the plain scalar. collapseFlowBlankLineRuns guards this case with isClosedFlow; the token-level path has no equivalent.
Reproduction
Each case above is yamlfmt.Formatter{}.Format(input, DefaultOptions()) on feat/3.0, comparing yaml.Unmarshal of the input against yaml.Unmarshal of the output. #1, #2 and #4 need no comparison — the output simply doesn't parse.
Note on coverage
pkg/formatter/yamlfmt has no CRLF test fixtures. Separately from the above, that gap hid a CRLF-flattening bug through several rounds of review on my #634 branch — worth adding CRLF coverage to the fixture set regardless of what happens with these six.
Problem
Six defects in the YAML formatter, all reproducible on
feat/3.0and all found while working on #634. None are caused by that work — I verified each against a pristine checkout — and none are fixed by the PR for it. Filing them together because they were found by one effort and cluster in the same area; happy to split into one issue per defect to match #632–#635 if you'd prefer.Four surfaced through differential fuzzing (pristine
feat/3.0compiled alongside a patched copy, asserting no data is lost that the baseline preserved). Two came from prettier comparison.Ordered by severity as I read it.
1. Output fails to parse — flow tag loses its separating space
The space after the
!00tag is stripped as trailing whitespace, and the result is no longer valid YAML:Worst of the six: the formatter emits a file the formatter itself will then reject.
2. Idempotency break —
---inside a plain scalar gets indentedA
---that is scalar content is treated as a document marker and reindented, so the second format pass fails. Found by the repo's ownFuzzYAMLFormatter.3. Nested sequence loses a level
[[0]]becomes[null, 0]— the nesting is flattened and a null appears.4. Explicit complex keys collapse into a duplicate-key collision
Every blank run is collapsed, so two keys that differ only by a blank line become identical, and the output fails to parse:
The blank lines are content here — YAML folds them into newlines in the key. Partially improved by the #634 PR (the output becomes parseable), not fully fixed.
5. Block scalar gains a trailing newline
Note this only shows up when the input has no final newline, so comparing the input's semantics against the output's requires care not to normalize the difference away.
6. Unterminated flow read as a plain-scalar continuation loses a blank line
The tokenizer emits
TokFlowfor the unterminated{, but yaml.v3 parses it as a continuation of the plain scalar.collapseFlowBlankLineRunsguards this case withisClosedFlow; the token-level path has no equivalent.Reproduction
Each case above is
yamlfmt.Formatter{}.Format(input, DefaultOptions())onfeat/3.0, comparingyaml.Unmarshalof the input againstyaml.Unmarshalof the output. #1, #2 and #4 need no comparison — the output simply doesn't parse.Note on coverage
pkg/formatter/yamlfmthas no CRLF test fixtures. Separately from the above, that gap hid a CRLF-flattening bug through several rounds of review on my #634 branch — worth adding CRLF coverage to the fixture set regardless of what happens with these six.