caddyserver/caddy · error

expanding TestCA endpoint '%s': %v

Error message

expanding TestCA endpoint '%s': %v

What it means

Returned by ACMEIssuer.Provision (modules/caddytls/acmeissuer.go:156) when the test/staging CA endpoint field (acme_ca_test, JSON "test_ca") contains a placeholder the replacer cannot resolve. The expansion runs with errorOnUnset, so a missing environment variable or bad placeholder syntax here stops issuer provisioning even if the production CA is fine.

Source

Thrown at modules/caddytls/acmeissuer.go:156

			return fmt.Errorf("expanding email address '%s': %v", iss.Email, err)
		}
		iss.Email = email
	}

	// expand CA endpoint, if non-empty
	if iss.CA != "" {
		ca, err := repl.ReplaceOrErr(iss.CA, true, true)
		if err != nil {
			return fmt.Errorf("expanding CA endpoint '%s': %v", iss.CA, err)
		}
		iss.CA = ca
	}

	// expand TestCA endpoint, if non-empty
	if iss.TestCA != "" {
		testca, err := repl.ReplaceOrErr(iss.TestCA, true, true)
		if err != nil {
			return fmt.Errorf("expanding TestCA endpoint '%s': %v", iss.TestCA, err)
		}
		iss.TestCA = testca
	}

	// expand EAB credentials, if non-empty
	if iss.ExternalAccount != nil {
		if iss.ExternalAccount.KeyID != "" {
			keyID, err := repl.ReplaceOrErr(iss.ExternalAccount.KeyID, true, true)
			if err != nil {
				return fmt.Errorf("expanding EAB key ID '%s': %v", iss.ExternalAccount.KeyID, err)
			}
			iss.ExternalAccount.KeyID = keyID
		}
		if iss.ExternalAccount.MACKey != "" {
			macKey, err := repl.ReplaceOrErr(iss.ExternalAccount.MACKey, true, true)
			if err != nil {
				return fmt.Errorf("expanding EAB MAC key (redacted): %v", err)
			}

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Define the variable in the environment the Caddy service runs in and restart
  2. Use the literal staging URL (https://acme-staging-v02.api.letsencrypt.org/directory) instead of a placeholder for this rarely-changing value
  3. Fix placeholder syntax to exactly {env.VAR_NAME}
  4. Remove acme_ca_test if staging is not actually needed

Example fix

# before
{
  acme_ca_test {env.STAGING_ENDPOINT}   # unset on this host
}

# after
{
  acme_ca_test https://acme-staging-v02.api.letsencrypt.org/directory
}
Defensive patterns

Strategy: validation

Validate before calling

if strings.Contains(cfgText, "{env.STAGING_CA}") && os.Getenv("STAGING_CA") == "" {
    return errors.New("STAGING_CA referenced by acme_ca_test but not set")
}

Try / catch

if err := issuer.Provision(ctx); err != nil {
    if strings.Contains(err.Error(), "expanding TestCA endpoint") {
        // set the env var or replace with the literal staging URL
    }
    return err
}

Prevention

When it happens

Trigger: Setting acme_ca_test {env.STAGING_CA} where STAGING_CA is unset in the Caddy process, or misspelled/unclosed placeholder syntax in the test endpoint value.

Common situations: Using Let's Encrypt staging in dev via an env var that only exists in CI; enabling debug/test mode configs on machines without the same environment; typos like {env.LE_STAGING}}.

Related errors


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