caddyserver/caddy · error

expanding EAB key ID '%s': %v

Error message

expanding EAB key ID '%s': %v

What it means

Returned by ACMEIssuer.Provision (modules/caddytls/acmeissuer.go:166) when an External Account Binding key ID (eab key_id / JSON "key_id") contains a placeholder that fails to expand. EAB is required by some ACME CAs (ZeroSSL, Google Trust Services, private CAs); a broken placeholder in the key ID prevents issuer setup entirely.

Source

Thrown at modules/caddytls/acmeissuer.go:166

		}
		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)
			}
			iss.ExternalAccount.MACKey = macKey
		}
	}

	// expand account key, if non-empty
	if iss.AccountKey != "" {
		accountKey, err := repl.ReplaceOrErr(iss.AccountKey, true, true)
		if err != nil {
			return fmt.Errorf("expanding account key PEM '%s': %v", iss.AccountKey, err)
		}

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Set EAB_KEY_ID in the actual service environment (systemd Environment=, EnvironmentFile=, or container env) and restart/reload Caddy
  2. Verify with: sudo systemctl show caddy -p Environment or printenv inside the container
  3. Fix the placeholder to exactly {env.EAB_KEY_ID}; no spaces, balanced braces
  4. If EAB is not required by your CA, remove the eab block entirely

Example fix

# before
 example.com {
   tls {
     issuer acme {
       eab {
         key_id {env.EAB_KEYID}   # actual var is EAB_KEY_ID
         mac_key {env.EAB_MAC_KEY}
       }
     }
   }
 }

# after
 example.com {
   tls {
     issuer acme {
       eab {
         key_id {env.EAB_KEY_ID}
         mac_key {env.EAB_MAC_KEY}
       }
     }
   }
 }
# systemd: EnvironmentFile=/etc/caddy/acme.env (defines both vars)
Defensive patterns

Strategy: validation

Validate before calling

// verify all EAB env vars exist before deploy
for _, k := range []string{"EAB_KEY_ID", "EAB_MAC_KEY"} {
    if os.Getenv(k) == "" {
        return fmt.Errorf("%s must be set for EAB-enabled ACME issuance", k)
    }
}

Try / catch

if err := issuer.Provision(ctx); err != nil {
    if strings.Contains(err.Error(), "expanding EAB key ID") {
        // EAB_KEY_ID missing: add to EnvironmentFile, then reload
    }
    return err
}

Prevention

When it happens

Trigger: Configuring tls { issuer acme { eab { key_id {env.EAB_KEY_ID} mac_key ... } } } where EAB_KEY_ID is not present in Caddy's environment, or the placeholder expression is malformed.

Common situations: Credentials stored in .env files that the systemd unit or container does not load; rotating EAB credentials in a secret manager but not updating the env var; copying configs to a new host without the secrets; brace typos.

Related errors


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