{"record":{"id":"1fe1a27bcf09ff92","repo":"slimtoolkit/slim","slug":"only-one-escape-parser-directive-can-be-used","errorCode":null,"errorMessage":"only one escape parser directive can be used","messagePattern":"only one escape parser directive can be used","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/docker/dockerfile/ast/parser.go","lineNumber":166,"sourceCode":"\td.escapeToken = rune(s[0])\n\td.lineContinuationRegex = regexp.MustCompile(`\\` + s + `[ \\t]*$`)\n\treturn nil\n}\n\n// possibleParserDirective looks for parser directives, eg '# escapeToken=<char>'.\n// Parser directives must precede any builder instruction or other comments,\n// and cannot be repeated.\nfunc (d *Directive) possibleParserDirective(line string) error {\n\tif d.processingComplete {\n\t\treturn nil\n\t}\n\n\ttecMatch := tokenEscapeCommand.FindStringSubmatch(strings.ToLower(line))\n\tif len(tecMatch) != 0 {\n\t\tfor i, n := range tokenEscapeCommand.SubexpNames() {\n\t\t\tif n == \"escapechar\" {\n\t\t\t\tif d.escapeSeen {\n\t\t\t\t\treturn errors.New(\"only one escape parser directive can be used\")\n\t\t\t\t}\n\t\t\t\td.escapeSeen = true\n\t\t\t\treturn d.setEscapeToken(tecMatch[i])\n\t\t\t}\n\t\t}\n\t}\n\n\td.processingComplete = true\n\treturn nil\n}\n\n// NewDefaultDirective returns a new Directive with the default escapeToken token\nfunc NewDefaultDirective() *Directive {\n\tdirective := Directive{}\n\tdirective.setEscapeToken(string(DefaultEscapeToken))\n\treturn &directive\n}\n","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/slimtoolkit/slim/blob/81940d17fa112cc678e32209214bcb2355cb3004/pkg/docker/dockerfile/ast/parser.go#L148-L184","documentation":"This error is raised by possibleParserDirective (called from processLine) when a Dockerfile contains more than one '# escape = <char>' parser directive. Parser directives must appear at the top of the file, before any instruction or comment, and may not be repeated.","triggerScenarios":"Writing two '# escape=\\' lines in the same Dockerfile (both before the first instruction); concatenating/merging Dockerfiles (e.g., in generated or templated files) that each included their own escape directive.","commonSituations":"Merging base Dockerfiles with FROM-scratch templates; code generators emitting the directive per-fragment; hand-edits duplicating the header block.","solutions":["Keep only one '# escape=' directive, at the very top of the Dockerfile","Remove the duplicate directive from merged/generated fragments","If different escaping is needed mid-file, restructure the Dockerfile instead of re-declaring the directive"],"exampleFix":"# before\n# escape=`\n# escape=`\nFROM alpine\n# after\n# escape=`\nFROM alpine","handlingStrategy":"validation","validationCode":"// Go: ensure at most one escape parser directive in the Dockerfile text\nfunc countEscapeDirectives(src string) int {\n    count, re := 0, regexp.MustCompile(`(?im)^#[ \\t]*escape[ \\t]*=`)\n    for range re.FindAllString(src, -1) { count++ }\n    return count\n}\nif countEscapeDirectives(dockerfile) > 1 {\n    return fmt.Errorf(\"duplicate # escape directive\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Declare '# escape=' at most once, at the very top of the Dockerfile","Strip per-fragment directives when merging generated Dockerfiles","Place the directive before any instruction or other comment so it is recognized at all"],"tags":["dockerfile","parsing","parser-directive","go"],"backgroundTag":"duplicate-parser-directive","analyzedSha":"81940d17fa112cc678e32209214bcb2355cb3004","analyzedAt":"2026-08-31T23:06:12.682Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}