{"record":{"id":"8e99c5f31bc793d9","repo":"caddyserver/caddy","slug":"acme-dns-specified-without-dns-provider-config-bu","errorCode":null,"errorMessage":"acme_dns specified without DNS provider config, but no provider specified with 'dns' global option","messagePattern":"acme_dns specified without DNS provider config, but no provider specified with 'dns' global option","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"caddyconfig/httpcaddyfile/tlsapp.go","lineNumber":548,"sourceCode":"\tglobalACMEEAB := options[\"acme_eab\"]\n\tglobalPreferredChains := options[\"preferred_chains\"]\n\tglobalCertLifetime := options[\"cert_lifetime\"]\n\tglobalHTTPPort, globalHTTPSPort := options[\"http_port\"], options[\"https_port\"]\n\tglobalDefaultBind := options[\"default_bind\"]\n\n\tif globalEmail != nil && acmeIssuer.Email == \"\" {\n\t\tacmeIssuer.Email = globalEmail.(string)\n\t}\n\tif globalACMECA != nil && acmeIssuer.CA == \"\" {\n\t\tacmeIssuer.CA = globalACMECA.(string)\n\t}\n\tif globalACMECARoot != nil && !slices.Contains(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string)) {\n\t\tacmeIssuer.TrustedRootsPEMFiles = append(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string))\n\t}\n\tif globalACMEDNSok && (acmeIssuer.Challenges == nil || acmeIssuer.Challenges.DNS == nil || acmeIssuer.Challenges.DNS.ProviderRaw == nil) {\n\t\tglobalDNS := options[\"dns\"]\n\t\tif globalDNS == nil && globalACMEDNS == nil {\n\t\t\treturn fmt.Errorf(\"acme_dns specified without DNS provider config, but no provider specified with 'dns' global option\")\n\t\t}\n\t\tif acmeIssuer.Challenges == nil {\n\t\t\tacmeIssuer.Challenges = new(caddytls.ChallengesConfig)\n\t\t}\n\t\tif acmeIssuer.Challenges.DNS == nil {\n\t\t\tacmeIssuer.Challenges.DNS = new(caddytls.DNSChallengeConfig)\n\t\t}\n\t\tif globalACMEDNS != nil && acmeIssuer.Challenges.DNS.ProviderRaw == nil {\n\t\t\t// Set a global DNS provider if `acme_dns` is set\n\t\t\tacmeIssuer.Challenges.DNS.ProviderRaw = caddyconfig.JSONModuleObject(globalACMEDNS, \"name\", globalACMEDNS.(caddy.Module).CaddyModule().ID.Name(), nil)\n\t\t}\n\t}\n\tif globalACMEEAB != nil && acmeIssuer.ExternalAccount == nil {\n\t\tacmeIssuer.ExternalAccount = globalACMEEAB.(*acme.EAB)\n\t}\n\tif globalPreferredChains != nil && acmeIssuer.PreferredChains == nil {\n\t\tacmeIssuer.PreferredChains = globalPreferredChains.(*caddytls.ChainPreference)\n\t}","sourceCodeStart":530,"sourceCodeEnd":566,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/caddyconfig/httpcaddyfile/tlsapp.go#L530-L566","documentation":"fillInGlobalACMEDefaults applies global 'acme_dns' to ACME issuers. parseOptDNS allows writing 'acme_dns' with NO arguments — it parses to nil, meaning 'inherit the globally-defined dns provider'. This error fires when that is the case (acme_dns key present, value nil) AND the issuer's DNS challenge has no provider AND options['dns'] is also nil: there is no provider anywhere to wire in, so the adapter errors instead of producing a providerless DNS challenge.","triggerScenarios":"A global block containing bare 'acme_dns' (no module name/args) while no global 'dns <provider> ...' option exists anywhere in the config.","commonSituations":"Copy-pasting an acme_dns line and losing its provider argument; intending to centralize the provider via 'dns' but forgetting that option; templates that emit 'acme_dns' unconditionally.","solutions":["Give acme_dns its provider: 'acme_dns cloudflare {env.CF_API_TOKEN}'.","Or keep bare 'acme_dns' and add the global 'dns cloudflare {env.CF_API_TOKEN}' option it inherits from.","Remove 'acme_dns' entirely if no DNS challenge is wanted.","Ensure the DNS provider plugin is in the build ('caddy list-modules | grep dns.providers')."],"exampleFix":"# before\n{\n  acme_dns\n}\n\n# after\n{\n  acme_dns cloudflare {env.CF_API_TOKEN}\n}\n\n# or\n{\n  dns cloudflare {env.CF_API_TOKEN}\n  acme_dns\n}","handlingStrategy":"validation","validationCode":"grep -nE '^\\s*acme_dns\\s*$' Caddyfile  # bare acme_dns with no provider and no global 'dns' option will fail","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always give acme_dns a provider argument, or pair bare acme_dns with a global dns provider.","Keep xcaddy build commands alongside the Caddyfile so DNS plugins are present."],"tags":["caddyfile","tls","acme","dns-challenge","global-options"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}