caddyserver/caddy · error
incomplete heredoc <<%s on line #%d, expected ending marker
Error message
incomplete heredoc <<%s on line #%d, expected ending marker %s
What it means
While lexing a heredoc (<<END ... END), the lexer reached end-of-input before seeing the closing marker line. The message names the marker and the line where the heredoc started, so you can find the unterminated block.
Source
Thrown at caddyconfig/caddyfile/lexer.go:128
var heredocMarker string
makeToken := func(quoted rune) bool {
l.token.Text = string(val)
l.token.wasQuoted = quoted
l.token.heredocMarker = heredocMarker
return true
}
for {
// Read a character in; if err then if we had
// read some characters, make a token. If we
// reached EOF, then no more tokens to read.
// If no EOF, then we had a problem.
ch, _, err := l.reader.ReadRune()
if err != nil {
if len(val) > 0 {
if inHeredoc {
return false, fmt.Errorf("incomplete heredoc <<%s on line #%d, expected ending marker %s", heredocMarker, l.line+l.skippedLines, heredocMarker)
}
return makeToken(0), nil
}
if err == io.EOF {
return false, nil
}
return false, err
}
// detect whether we have the start of a heredoc
if (!quoted && !btQuoted) && (!inHeredoc && !heredocEscaped) &&
len(val) > 1 && string(val[:2]) == "<<" {
// a space means it's just a regular token and not a heredoc
if ch == ' ' {
return makeToken(0), nil
}
View on GitHub (pinned to 50e54ee279)
Solutions
- Add the closing marker on its own line, exactly matching the opening marker, no leading/trailing whitespace
- Check for trailing spaces or CR characters on the closing marker line (cat -A)
- Confirm the marker only uses alphanumerics, dashes, underscores
- Ensure the file ends with a newline after the marker
Example fix
# before respond <<HTML <p>hi</p> # (EOF, no closing marker) # after respond <<HTML <p>hi</p> HTML
Defensive patterns
Strategy: validation
Validate before calling
// before adapt: every '<<MARKER' opener must have a line equal to MARKER
type validator struct{ marker string; closed bool }
func checkHeredocs(lines []string) error {
var open string
for _, ln := range lines {
t := strings.TrimSpace(ln)
if open == "" {
if strings.HasPrefix(t, "<<") { open = strings.TrimPrefix(t, "<<") }
} else if t == open {
open = ""
}
}
if open != "" { return fmt.Errorf("heredoc %s never closed", open) }
return nil
} Prevention
- Always close heredocs with the bare marker at the desired indentation
- Disable trailing-whitespace trimming for Caddyfiles in your editor
- Prefer quoted respond bodies over heredocs for short strings
When it happens
Trigger: A Caddyfile contains <<MARKER but no subsequent line equal to MARKER before EOF. This can be a missing closing marker, a marker with trailing characters/spaces, or CR/LF mismatches making the closing line not match exactly.
Common situations: Copy-pasting heredoc config (e.g. inline HTML or TLS policies) and dropping the last line; using an editor that trims trailing whitespace on the closing marker; Windows line endings where the marker line has trailing '\r'.
Related errors
- missing opening heredoc marker on line #%d; must contain onl
- too many '<' for heredoc on line #%d; only use two, for exam
- heredoc marker on line #%d must contain only alphanumeric ch
- mismatched leading whitespace in heredoc <<%s on line #%d [%
- invalid action type
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/db0d4134a18d77df.
Report an issue: GitHub.