{"record":{"id":"0273cc8263714822","repo":"caddyserver/caddy","slug":"heredoc-marker-on-line-d-must-contain-only-alpha","errorCode":null,"errorMessage":"heredoc marker on line #%d must contain only alphanumeric characters, dashes and underscores; got '%s'","messagePattern":"heredoc marker on line #(.+?) must contain only alphanumeric characters, dashes and underscores; got '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"caddyconfig/caddyfile/lexer.go","lineNumber":168,"sourceCode":"\t\t\t}\n\n\t\t\t// after hitting a newline, we know that the heredoc marker\n\t\t\t// is the characters after the two << and the newline.\n\t\t\t// we reset the val because the heredoc is syntax we don't\n\t\t\t// want to keep.\n\t\t\tif ch == '\\n' {\n\t\t\t\tif len(val) == 2 {\n\t\t\t\t\treturn false, fmt.Errorf(\"missing opening heredoc marker on line #%d; must contain only alphanumeric characters, dashes and underscores; got empty string\", l.line)\n\t\t\t\t}\n\n\t\t\t\t// check if there's too many <\n\t\t\t\tif string(val[:3]) == \"<<<\" {\n\t\t\t\t\treturn false, fmt.Errorf(\"too many '<' for heredoc on line #%d; only use two, for example <<END\", l.line)\n\t\t\t\t}\n\n\t\t\t\theredocMarker = string(val[2:])\n\t\t\t\tif !heredocMarkerRegexp.Match([]byte(heredocMarker)) {\n\t\t\t\t\treturn false, fmt.Errorf(\"heredoc marker on line #%d must contain only alphanumeric characters, dashes and underscores; got '%s'\", l.line, heredocMarker)\n\t\t\t\t}\n\n\t\t\t\tinHeredoc = true\n\t\t\t\tl.skippedLines++\n\t\t\t\tval = nil\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tval = append(val, ch)\n\t\t\tcontinue\n\t\t}\n\n\t\t// if we're in a heredoc, all characters are read as-is\n\t\tif inHeredoc {\n\t\t\tval = append(val, ch)\n\n\t\t\tif ch == '\\n' {\n\t\t\t\tl.skippedLines++\n\t\t\t}","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/caddyconfig/caddyfile/lexer.go#L150-L186","documentation":"The heredoc marker (text between << and end of the opening line) contains characters outside [A-Za-z0-9_-]. The lexer enforces this via heredocMarkerRegexp so the closing marker line is unambiguous.","triggerScenarios":"Opening a heredoc with a marker containing spaces, dots, quotes, or other symbols, e.g. <<E.OF or <<\"EOF\" (quoted markers as in bash are not supported).","commonSituations":"Porting bash heredoc syntax with quoted delimiters (<<'EOF'), or markers containing punctuation like '.' or '/'.","solutions":["Rename the marker to only letters, digits, dashes, underscores (e.g. <<EOF or <<HTML_END)","Use the same marker on the closing line","Do not quote the marker"],"exampleFix":"# before\nrespond <<'EOF'\n# after\nrespond <<EOF","handlingStrategy":"validation","validationCode":"var markerRe = regexp.MustCompile(`^[A-Za-z0-9_-]+$`)\nif !markerRe.MatchString(marker) {\n    return fmt.Errorf(\"bad heredoc marker %q\", marker)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use plain markers like EOF, HTML, JSON_END","Never quote heredoc markers in Caddyfiles"],"tags":["caddyfile","heredoc","lexer"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}