caddyserver/caddy · error

loading on-demand TLS permission module: %v

Error message

loading on-demand TLS permission module: %v

What it means

When tls.automation.on_demand.permission is configured, ctx.LoadModule loads that permission module; failure to load or provision it produces this error. Causes: unknown module name under 'permission', module inline config rejected by the module's own Provision, or the module not being compiled into the binary.

Source

Thrown at modules/caddytls/tls.go:284

			return fmt.Errorf("loading certificates: %v", err)
		}
		for _, cert := range certs {
			hash, err := magic.CacheUnmanagedTLSCertificate(ctx, cert.Certificate, cert.Tags)
			if err != nil {
				return fmt.Errorf("caching unmanaged certificate: %v", err)
			}
			t.loaded[hash] = ""
		}
	}

	// on-demand permission module
	if t.Automation != nil && t.Automation.OnDemand != nil && t.Automation.OnDemand.PermissionRaw != nil {
		if t.Automation.OnDemand.Ask != "" {
			return fmt.Errorf("on-demand TLS config conflict: both 'ask' endpoint and a 'permission' module are specified; 'ask' is deprecated, so use only the permission module")
		}
		val, err := ctx.LoadModule(t.Automation.OnDemand, "PermissionRaw")
		if err != nil {
			return fmt.Errorf("loading on-demand TLS permission module: %v", err)
		}
		t.Automation.OnDemand.permission = val.(OnDemandPermission)
	}

	// automation/management policies
	if t.Automation == nil {
		t.Automation = new(AutomationConfig)
	}
	t.Automation.defaultPublicAutomationPolicy = new(AutomationPolicy)
	err = t.Automation.defaultPublicAutomationPolicy.Provision(t)
	if err != nil {
		return fmt.Errorf("provisioning default public automation policy: %v", err)
	}
	for n := range t.automateNames {
		// if any names specified by the "automate" loader do not qualify for a public
		// certificate, we should initialize a default internal automation policy
		// (but we don't want to do this unnecessarily, since it may prompt for password!)
		if certmagic.SubjectQualifiesForPublicCert(n) {

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Read the wrapped error — it comes from the module's own loader/Provision
  2. For the built-in 'http' module, ensure endpoint is a fully-qualified URL with scheme and host
  3. Verify module availability with 'caddy list-modules | grep permission'
  4. Validate the config with 'caddy validate' before deploying

Example fix

// before
"permission": {"module": "http", "endpoint": "localhost:5555/check"}
// after
"permission": {"module": "http", "endpoint": "http://localhost:5555/check"}
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(onDemandPermissionEndpoint)
if err != nil || u.Scheme == "" || u.Host == "" {
	return fmt.Errorf("permission endpoint must be absolute URL")
}

Prevention

When it happens

Trigger: {"permission": {"module": "http", "endpoint": "not a valid url"}} — the http permission module validates its endpoint and fails; or a custom permission module name that is not registered; or a nil/empty module object.

Common situations: Typos in the endpoint URL (missing scheme, spaces); forgetting that the permission module shape is {module: name, ...options}; using a plugin not included in the build.

Understand the failure class

Related errors


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