{"record":{"id":"eb78f5707e9e637d","repo":"caddyserver/caddy","slug":"expanding-account-key-pem-s-v","errorCode":null,"errorMessage":"expanding account key PEM '%s': %v","messagePattern":"expanding account key PEM '(.+?)': (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddytls/acmeissuer.go","lineNumber":183,"sourceCode":"\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"expanding EAB key ID '%s': %v\", iss.ExternalAccount.KeyID, err)\n\t\t\t}\n\t\t\tiss.ExternalAccount.KeyID = keyID\n\t\t}\n\t\tif iss.ExternalAccount.MACKey != \"\" {\n\t\t\tmacKey, err := repl.ReplaceOrErr(iss.ExternalAccount.MACKey, true, true)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"expanding EAB MAC key (redacted): %v\", err)\n\t\t\t}\n\t\t\tiss.ExternalAccount.MACKey = macKey\n\t\t}\n\t}\n\n\t// expand account key, if non-empty\n\tif iss.AccountKey != \"\" {\n\t\taccountKey, err := repl.ReplaceOrErr(iss.AccountKey, true, true)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"expanding account key PEM '%s': %v\", iss.AccountKey, err)\n\t\t}\n\t\tiss.AccountKey = accountKey\n\t}\n\n\t// expand DNS override domain, if non-empty\n\tif iss.Challenges != nil && iss.Challenges.DNS != nil && iss.Challenges.DNS.OverrideDomain != \"\" {\n\t\toverrideDomain, err := repl.ReplaceOrErr(iss.Challenges.DNS.OverrideDomain, true, true)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"expanding DNS override domain '%s': %v\", iss.Challenges.DNS.OverrideDomain, err)\n\t\t}\n\t\tiss.Challenges.DNS.OverrideDomain = overrideDomain\n\t}\n\n\t// DNS challenge provider, if not already established\n\tif iss.Challenges != nil && iss.Challenges.DNS != nil && iss.Challenges.DNS.solver == nil {\n\t\tvar prov certmagic.DNSProvider\n\t\tif iss.Challenges.DNS.ProviderRaw != nil {\n\t\t\t// a challenge provider has been locally configured - use it","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddytls/acmeissuer.go#L165-L201","documentation":"Returned by ACMEIssuer.Provision (modules/caddytls/acmeissuer.go:183) when the ACME account key PEM field (JSON \"account_key\", a full private key embedded in config) contains a placeholder that fails to expand. Caddy allows the account key to be supplied via placeholders (e.g. {env.ACME_ACCOUNT_KEY} or a file read via {http...}/{file...} style replacements), and an unresolvable one is a hard error.","triggerScenarios":"Setting \"account_key\": \"{env.ACCOUNT_KEY_PEM}\" on an ACME issuer where the environment variable is missing, or where the placeholder is misspelled; the error occurs even though the raw PEM is never echoed (the message includes iss.AccountKey, i.e. the unexpanded placeholder text, not a secret value, when the placeholder itself is the configured string).","commonSituations":"Pinning a pre-created ACME account across restarts/clusters by injecting its key via env or secrets; var present on the old node but not on new nodes during scaling; multi-line PEM stored in env vars mangled by YAML/compose folding.","solutions":["Ensure the env var/secret holding the PEM exists on every node running Caddy and restart/reload","Verify the var contains a valid single-line-escaped or raw PEM that certmagic can decode after expansion; test: printenv ACCOUNT_KEY_PEM | openssl pkey -noout","Fix placeholder syntax to exactly {env.VAR}","If you do not need a pinned account key, remove the field - Caddy/certmagic will generate and persist one in storage"],"exampleFix":"// before\n\"issuer\": {\n  \"module\": \"acme\",\n  \"account_key\": \"{env.ACCT_KEY}\"   // ACCT_KEY unset\n}\n\n// after\n\"issuer\": {\n  \"module\": \"acme\"\n}\n// let certmagic manage the account key in storage, or set ACCT_KEY to the PEM in the service env","handlingStrategy":"validation","validationCode":"// if pinning an account key, verify the env var holds a parseable PEM first\nif v := os.Getenv(\"ACCT_KEY\"); v != \"\" {\n    if _, err := certmagic.PEMDecodePrivateKey([]byte(v)); err != nil {\n        return fmt.Errorf(\"ACCT_KEY is not a valid private key PEM: %v\", err)\n    }\n} else if strings.Contains(cfgText, \"{env.ACCT_KEY}\") {\n    return errors.New(\"ACCT_KEY referenced but not set\")\n}","typeGuard":null,"tryCatchPattern":"if err := issuer.Provision(ctx); err != nil {\n    if strings.Contains(err.Error(), \"expanding account key PEM\") {\n        // account key placeholder unresolved: set env var or drop the field\n    }\n    return err\n}","preventionTips":["Prefer letting certmagic manage the account key in storage unless you need cross-node account sharing","If pinning, distribute the key via secrets, and validate it parses (openssl pkey -noout) on each node","Ensure multi-line PEMs survive transport (base64-wrap if your env layer strips newlines)"],"tags":["tls","acme","account-key","placeholders","secrets"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}