caddy fmt deletes an opening brace at the end of the input
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 88/100
Research direction
Start in caddyconfig/caddyfile/formatter.go around the brace handling referenced in the issue, then read TestFormatter for the existing formatter cases. Reproduce the examples with caddy fmt and verify the added cases preserve a trailing opening brace. Confirm the formatter remains idempotent and run the relevant Caddyfile tests.
Written by the indexing model from the issue text.
Description
Issue Details
caddyfile.Format silently deletes an opening brace when the brace is the last non-space character of the input, so caddy fmt removes a character the user typed.
input "localhost {" -> output "localhost\n"
input "{" -> output "\n"
input "{ {" -> output "{\n"
Reproduce with the CLI:
printf 'localhost {' > Caddyfile
caddy fmt Caddyfile
localhost
{"level":"error","ts":...,"msg":"Caddyfile:1: Caddyfile input is not formatted; Tip: use '--overwrite' to update your Caddyfile in-place instead of previewing it. Consult '--help' for more options"}
The brace is gone from the preview, and the message then points the user at --overwrite, which writes that result back over their file.
This matters most with format-on-save, and with caddy fmt --overwrite the deletion is written back to the user's file. A Caddyfile whose last line has just opened a block is a normal intermediate state while editing, and formatting it should not drop the brace.
It also breaks idempotence, which this repo already checks for in caddyconfig/caddyfile/formatter_fuzz.go:
Format("{ {") -> "{\n"
Format("{\n") -> "\n"
The reason that fuzz target never reported it is that it only uses the idempotence result as a go-fuzz corpus priority (return 1 / return 0), so a non-idempotent input is deprioritised rather than reported as a failure.
Cause
Format writes { lazily. On case ch == '{' it records openBrace = true / openBraceWritten = false and continues, and the brace is only emitted later, when a following regular character is processed:
https://github.com/caddyserver/caddy/blob/master/caddyconfig/caddyfile/formatter.go#L355-L358
If the input ends while a brace is still pending, the loop exits and nothing flushes it, so the brace is never written. There is no flush after the loop; the function goes straight to bytes.TrimSpace and returns.
Proposed fix
Flush a pending brace after the read loop ends. I have this working locally as a four-line change, with two cases added to TestFormatter, and the full test suite passes. Happy to open a PR if you would like it.
Assistance Disclosure
AI used
If AI was used, describe the extent to which it was used.
I used Claude Code to fuzz the Caddyfile formatter for idempotence, which is how this was found, and to help trace the root cause and draft the fix and the test cases. I reviewed the change, confirmed the behaviour before and after it, and ran the test suite locally.
- Dominant language
- Go
- Stars
- 75.9k
- Forks
- 5k
- Avg merge
- 3d 6h
- Merged PRs (30d)
- 28
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from caddyserver/caddy
-
bug :lady_beetle: help wanted :sos:
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
caddyserver/caddy#8037 · 2 comments ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
caddyserver/caddy#8016 ·
-
bug :lady_beetle:
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
caddyserver/caddy#7784 · 5 comments ·
-
Difficulty 3/5 1-2 days Newbie friendliness 72/100
caddyserver/caddy#8044 ·
-
reverse_proxy with versions 3 silently ignores tls_trust_pool and verifies against the system roots Open
Difficulty 3/5 1-2 days Newbie friendliness 68/100
caddyserver/caddy#8041 · 7 comments ·
All issues in caddyserver/caddy
Similar issues
-
Difficulty 1/5 Under an hour Newbie friendliness 84/100
-
enhancement needs triage
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
kind/cleanup
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
kubernetes-sigs/kueue#15947 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
sympozium-ai/sympozium#627 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100