caddyserver/caddy · error

%w, at %s:%d import chain ['%s']

Error message

%w, at %s:%d import chain ['%s']

What it means

WrapErr decorates any Caddyfile parse error with the file and line of the current token; this variant fires when the token came from an imported file (via import), appending the full import chain so you can trace which snippet imported the failing line.

Source

Thrown at caddyconfig/caddyfile/dispenser.go:445

// the end of the input when searching for the next token.
func (d *Dispenser) EOFErr() error {
	return d.Errf("unexpected EOF")
}

// Err generates a custom parse-time error with a message of msg.
func (d *Dispenser) Err(msg string) error {
	return d.WrapErr(errors.New(msg))
}

// Errf is like Err, but for formatted error messages
func (d *Dispenser) Errf(format string, args ...any) error {
	return d.WrapErr(fmt.Errorf(format, args...))
}

// WrapErr takes an existing error and adds the Caddyfile file and line number.
func (d *Dispenser) WrapErr(err error) error {
	if len(d.Token().imports) > 0 {
		return fmt.Errorf("%w, at %s:%d import chain ['%s']", err, d.File(), d.Line(), strings.Join(d.Token().imports, "','"))
	}
	return fmt.Errorf("%w, at %s:%d", err, d.File(), d.Line())
}

// Delete deletes the current token and returns the updated slice
// of tokens. The cursor is not advanced to the next token.
// Because deletion modifies the underlying slice, this method
// should only be called if you have access to the original slice
// of tokens and/or are using the slice of tokens outside this
// Dispenser instance. If you do not re-assign the slice with the
// return value of this method, inconsistencies in the token
// array will become apparent (or worse, hide from you like they
// did me for 3 and a half freaking hours late one night).
func (d *Dispenser) Delete() []Token {
	if d.cursor >= 0 && d.cursor <= len(d.tokens)-1 {
		d.tokens = append(d.tokens[:d.cursor], d.tokens[d.cursor+1:]...)
		d.cursor--
	}

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Open the file:line in the message; the import chain after 'import chain' lists the hops from the main Caddyfile
  2. Fix the underlying error described before the ', at file:line' part
  3. If the chain is confusing, temporarily inline the imported file to get a flat error
  4. Check for stale imported files that reference directives removed in your Caddy version

Example fix

# before: snippets/site.caddy has a typo
root * /vr-srv  # inside import snippets/*.caddy
# after
root * /var/www
Defensive patterns

Strategy: try-catch

Try / catch

if err := serverType.Setup(serverBlocks); err != nil {
    var line, file string
    if _, e := fmt.Sscanf(err.Error(), "%s at %s", &line, &file); e == nil {
        // surface file/line and import chain to the user in CI output
    }
}

Prevention

When it happens

Trigger: Any Caddyfile error constructed with d.Err/d.Errf/d.WrapErr where the current token has a non-empty imports list — i.e. the error is inside a file or snippet reached through one or more import statements.

Common situations: A syntax error or unrecognized directive inside an imported snippet (e.g. site snippets in a separate file pulled in with import blocks/*.caddy). The import chain names each hop, so files deep in an import tree are debuggable.

Related errors


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/7ffd6529bd38364e. Report an issue: GitHub.