caddyserver/caddy · error

could not convert automation policy subject '%s' to punycode

Error message

could not convert automation policy subject '%s' to punycode: %v

What it means

During AutomationPolicy.Provision, each subject (domain name) is passed through the placeholder replacer and then idna.ToASCII to convert it to punycode/ASCII form. If the conversion fails, the subject is not a valid IDNA name. The wrapped error from the idna library explains the exact problem (empty label, disallowed rune, label too long, etc.).

Source

Thrown at modules/caddytls/automation.go:190

	subjects []string
	magic    *certmagic.Config
	storage  certmagic.Storage

	// Whether this policy had explicit managers configured directly on it.
	hadExplicitManagers bool
}

// Provision sets up ap and builds its underlying CertMagic config.
func (ap *AutomationPolicy) Provision(tlsApp *TLS) error {
	// replace placeholders in subjects to allow environment variables
	repl := caddy.NewReplacer()
	subjects := make([]string, len(ap.SubjectsRaw))
	for i, sub := range ap.SubjectsRaw {
		sub = repl.ReplaceAll(sub, "")
		subASCII, err := idna.ToASCII(sub)
		if err != nil {
			return fmt.Errorf("could not convert automation policy subject '%s' to punycode: %v", sub, err)
		}
		subjects[i] = subASCII
	}
	ap.subjects = subjects

	// policy-specific storage implementation
	if ap.StorageRaw != nil {
		val, err := tlsApp.ctx.LoadModule(ap, "StorageRaw")
		if err != nil {
			return fmt.Errorf("loading TLS storage module: %v", err)
		}
		cmStorage, err := val.(caddy.StorageConverter).CertMagicStorage()
		if err != nil {
			return fmt.Errorf("creating TLS storage configuration: %v", err)
		}
		ap.storage = cmStorage
	}

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Inspect the subject named in the message and fix or remove the invalid character(s) — the wrapped idna error states the precise cause.
  2. If the subject uses {$ENV_PLACEHOLDER} syntax, confirm the environment variable is set to a valid hostname on the machine running Caddy.
  3. Validate names with a punycode converter (e.g. idn command or an online IDNA tool) before putting them in config.
  4. Remember underscores are not permitted in hostnames per IDNA; remove them or move such names out of the TLS subjects.

Example fix

# before (placeholder unset -> empty/invalid subject)
{$SITE_DOMAIN}:443 {
	respond "hi"
}

# after (valid literal or verified env var)
example.com:443 {
	respond "hi"
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate subjects before provisioning.
for _, s := range policy.SubjectsRaw {
    s = repl.ReplaceAll(s, "")
    if _, err := idna.ToASCII(s); err != nil {
        return fmt.Errorf("subject %q fails IDNA: %v", s, err)
    }
}

Type guard

func isValidSubjectName(s string) bool {
    _, err := idna.ToASCII(s)
    return err == nil
}

Prevention

When it happens

Trigger: A subject in the automation policy containing characters that cannot be IDNA-encoded: strings longer than 253 bytes, labels over 63 chars, empty labels (double dots), stray underscores placed where IDNA forbids them, or a placeholder that expanded to an empty/invalid name.

Common situations: Typos in Caddyfile site addresses (double dots, trailing garbage); environment placeholders like {$DOMAIN} unset so the subject collapses to an empty string; pasting Unicode hostnames with zero-width characters; copy/paste of wildcard names with incorrect syntax.

Related errors


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