{"record":{"id":"09fb6372371e083d","repo":"caddyserver/caddy","slug":"mismatched-leading-whitespace-in-heredoc-s-on-l","errorCode":null,"errorMessage":"mismatched leading whitespace in heredoc <<%s on line #%d [%s], expected whitespace [%s] to match the closing marker","messagePattern":"mismatched leading whitespace in heredoc <<(.+?) on line #(.+?) \\[(.+?)\\], expected whitespace \\[(.+?)\\] to match the closing marker","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"caddyconfig/caddyfile/lexer.go","lineNumber":327,"sourceCode":"\t// figure out how much whitespace we need to strip from the front of every line\n\t// by getting the string that precedes the marker, on the last line\n\tpaddingToStrip := stringVal[lastNewline+1 : len(stringVal)-len(marker)]\n\n\t// iterate over each line and strip the whitespace from the front\n\tvar out string\n\tfor lineNum, lineText := range lines[:len(lines)-1] {\n\t\tif lineText == \"\" || lineText == \"\\r\" {\n\t\t\tout += \"\\n\"\n\t\t\tcontinue\n\t\t}\n\n\t\t// find an exact match for the padding\n\t\tindex := strings.Index(lineText, paddingToStrip)\n\n\t\t// if the padding doesn't match exactly at the start then we can't safely strip\n\t\tif index != 0 {\n\t\t\tcleanLineText := strings.TrimRight(lineText, \"\\r\\n\")\n\t\t\treturn nil, fmt.Errorf(\"mismatched leading whitespace in heredoc <<%s on line #%d [%s], expected whitespace [%s] to match the closing marker\", marker, l.line+lineNum+1, cleanLineText, paddingToStrip)\n\t\t}\n\n\t\t// strip, then append the line, with the newline, to the output.\n\t\t// also removes all \"\\r\" because Windows.\n\t\tout += strings.ReplaceAll(lineText[len(paddingToStrip):]+\"\\n\", \"\\r\", \"\")\n\t}\n\n\t// Remove the trailing newline from the loop\n\tif len(out) > 0 && out[len(out)-1] == '\\n' {\n\t\tout = out[:len(out)-1]\n\t}\n\n\t// return the final value\n\treturn []rune(out), nil\n}\n\n// Quoted returns true if the token was enclosed in quotes\n// (i.e. double quotes, backticks, or heredoc).","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/caddyconfig/caddyfile/lexer.go#L309-L345","documentation":"In a heredoc, Caddy strips the closing marker's leading indentation from every content line. If a content line's leading whitespace does not exactly start with that padding (wrong character, mixed tabs/spaces, or less indentation than the marker), stripping is unsafe and this error fires, naming the offending line and expected padding.","triggerScenarios":"Closing marker indented (e.g. two spaces before END) while some content line uses a tab, fewer spaces, or different whitespace than the marker's indentation. strings.Index(lineText, paddingToStrip) != 0.","commonSituations":"Mixed tabs and spaces from copy-pasting; editor auto-indent changing one line; a content line at lower indentation than the closing marker.","solutions":["Make every content line's leading whitespace use the same characters and at least the depth of the closing marker's indentation","Avoid mixing tabs and spaces inside heredocs","Simplest fix: put the closing marker at column 0 and left-align content, or indent both consistently","Check the reported line number and expected-vs-actual whitespace shown in brackets in the message"],"exampleFix":"# before: marker indented 2 spaces, content line uses a tab\n  respond <<HTML\n\t<p>hi</p>\n  HTML\n# after\n  respond <<HTML\n  \t<p>hi</p>  <- replace tab with 2+ spaces matching marker padding\n  HTML","handlingStrategy":"validation","validationCode":"// before adapt: verify every heredoc content line starts with the closing marker's padding\nfunc checkPadding(content, marker string) error {\n    pad := marker[:len(marker)-len(strings.TrimLeft(marker, \" \\t\"))]\n    for i, ln := range strings.Split(content, \"\\n\") {\n        if ln != \"\" && !strings.HasPrefix(ln, pad) {\n            return fmt.Errorf(\"line %d lacks padding %q\", i+1, pad)\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use spaces only inside heredocs, no tabs","Indent content at least as far as the closing marker","Run `caddy adapt` after re-indenting heredoc blocks"],"tags":["caddyfile","heredoc","whitespace","lexer"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}