slimtoolkit/slim · error
invalid ESCAPE '%s'. Must be ` or \
Error message
invalid ESCAPE '%s'. Must be ` or \
What it means
The Dockerfile parser's setEscapeToken validates the value of a parser-directive ESCAPE (# escape=` or # escape=\). Only the backtick and backslash are legal escape tokens (matching Docker's spec); any other character makes the parser return this error. The escape token controls line-continuation and character escaping during parsing.
Source
Thrown at pkg/docker/dockerfile/ast/parser.go:146
tokenComment = regexp.MustCompile(`^#.*$`)
)
// DefaultEscapeToken is the default escape token
const DefaultEscapeToken = '\\'
// Directive is the structure used during a build run to hold the state of
// parsing directives.
type Directive struct {
escapeToken rune // Current escape token
lineContinuationRegex *regexp.Regexp // Current line continuation regex
processingComplete bool // Whether we are done looking for directives
escapeSeen bool // Whether the escape directive has been seen
}
// setEscapeToken sets the default token for escaping characters in a Dockerfile.
func (d *Directive) setEscapeToken(s string) error {
if s != "`" && s != "\\" {
return fmt.Errorf("invalid ESCAPE '%s'. Must be ` or \\", s)
}
d.escapeToken = rune(s[0])
d.lineContinuationRegex = regexp.MustCompile(`\` + s + `[ \t]*$`)
return nil
}
// possibleParserDirective looks for parser directives, eg '# escapeToken=<char>'.
// Parser directives must precede any builder instruction or other comments,
// and cannot be repeated.
func (d *Directive) possibleParserDirective(line string) error {
if d.processingComplete {
return nil
}
tecMatch := tokenEscapeCommand.FindStringSubmatch(strings.ToLower(line))
if len(tecMatch) != 0 {
for i, n := range tokenEscapeCommand.SubexpNames() {
if n == "escapechar" {View on GitHub (pinned to 81940d17fa)
Solutions
- Change the directive to a legal value: `# escape=\` (default) or `# escape=\`` (backtick, useful on Windows).
- Remove the escape directive entirely if you don't need a custom escape token.
- Fix typos/case: the directive must be `# escape=<char>` with exactly one legal character and no quotes.
- Re-run the build/parser after fixing; the error names the offending character in %s.
Example fix
# before # escape=" FROM ubuntu # after # escape=` FROM ubuntu
Defensive patterns
Strategy: validation
Validate before calling
re := regexp.MustCompile(`(?m)^#\s*escape\s*=\s*(.)\s*$`)
if m := re.FindStringSubmatch(dockerfile); m != nil && m[1] != "`" && m[1] != "\\" {
return fmt.Errorf("invalid escape directive %q; use ` or \\", m[1])
} Try / catch
err := parser.Parse(strings.NewReader(dockerfile))
if err != nil {
if strings.Contains(err.Error(), "invalid ESCAPE") {
dockerfile = fixEscapeDirective(dockerfile) // rewrite to `# escape=\`
err = parser.Parse(strings.NewReader(dockerfile))
}
return err
} Prevention
- Only use `# escape=\` or `# escape=`` in Dockerfiles.
- Never quote the escape value; it must be a single unquoted character.
- Lint Dockerfiles with hadolint or a parse preflight in CI.
- Watch editor auto-substitutions that replace backslashes/backticks with lookalikes.
When it happens
Trigger: Parsing a Dockerfile containing a directive like `# escape='` or `# escape=x` with an unsupported character, or a malformed escape directive line whose extracted value isn't exactly ` or \.
Common situations: Copy-pasted Dockerfiles with a typo'd escape directive; editors converting backticks/backslashes; using `# escape="` thinking quotes are allowed; confusion with the case-sensitive directive name/value.
Related errors
- file path is not absolute
- file is not a binary
- when using JSON array syntax, arrays must be comprised of st
- unknown instruction
- only one escape parser directive can be used
AI-assisted analysis of slimtoolkit/slim@81940d17fa (2026-08-31).
Data as JSON: /api/errors/0c3bce2297680d1c.
Report an issue: GitHub.