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

  1. Change the directive to a legal value: `# escape=\` (default) or `# escape=\`` (backtick, useful on Windows).
  2. Remove the escape directive entirely if you don't need a custom escape token.
  3. Fix typos/case: the directive must be `# escape=<char>` with exactly one legal character and no quotes.
  4. 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

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


AI-assisted analysis of slimtoolkit/slim@81940d17fa (2026-08-31). Data as JSON: /api/errors/0c3bce2297680d1c. Report an issue: GitHub.