caddyserver/caddy · error · caddy.APIError

failed to provision CA %s, %w

Error message

failed to provision CA %s, %w

What it means

A 500 caddy.APIError returned when the default CA (`local`) was requested but not yet configured, so the admin API tries to provision it on demand (ca.Provision) and that fails. Provisioning generates/loads the root and intermediate from storage, so failures are storage errors (cannot write keys/certs), crypto errors, or missing dependencies. The underlying error is wrapped with %w.

Source

Thrown at modules/caddypki/adminapi.go:212

	// provision it, because the user probably aims to
	// change their config to enable PKI immediately after
	// if they actually requested the local CA ID.
	if !ok {
		if id != DefaultCAID {
			return nil, caddy.APIError{
				HTTPStatus: http.StatusNotFound,
				Err:        fmt.Errorf("no certificate authority configured with id: %s", id),
			}
		}

		// Provision the default CA, which generates and stores a root
		// certificate in storage, if one doesn't already exist.
		ca = new(CA)
		err := ca.Provision(a.ctx, id, a.log)
		if err != nil {
			return nil, caddy.APIError{
				HTTPStatus: http.StatusInternalServerError,
				Err:        fmt.Errorf("failed to provision CA %s, %w", id, err),
			}
		}
	}

	return ca, nil
}

func rootAndIntermediatePEM(ca *CA) (root, inter []byte, err error) {
	root, err = pemEncodeCert(ca.RootCertificate().Raw)
	if err != nil {
		return root, inter, err
	}

	for _, interCert := range ca.IntermediateCertificateChain() {
		pemBytes, err := pemEncodeCert(interCert.Raw)
		if err != nil {
			return nil, nil, err
		}

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Read the wrapped cause in the response body — it distinguishes storage errors from crypto errors
  2. Fix storage access: permissions on the storage root (chown to the Caddy user), connectivity/credentials for custom storage modules
  3. If provisioning genuinely failed midway, remove the partial CA resources under the storage (pki directories) and retry the request to regenerate
  4. Alternatively configure the pki app explicitly in config so provisioning happens (and fails loudly) at load time with fuller logs

Example fix

# before: storage dir owned by root, default CA fails to provision
curl http://localhost:2019/pki/ca/local   # 500 failed to provision CA local

# after
sudo chown -R caddy:caddy /var/lib/caddy   # or the storage root in use
curl http://localhost:2019/pki/ca/local
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight storage writability before first use of the default CA:
func storageWritable() error {
    dir := filepath.Join(caddy.AppDataDir())
    f, err := os.CreateTemp(dir, "pki-probe-*")
    if err != nil { return fmt.Errorf("storage not writable: %w", err) }
    f.Close()
    os.Remove(f.Name())
    return nil
}

Try / catch

// On 500 'failed to provision CA', read the wrapped cause before acting:
if resp.StatusCode == 500 && strings.Contains(string(body), "failed to provision") {
    // storage/permission issue — fix environment, then retry the same GET
    return fmt.Errorf("default CA provisioning failed: %s; check storage perms", body)
}

Prevention

When it happens

Trigger: GET /pki/ca/local (or /pki/ca/local/certificates) with no pki app configured, where Provision fails: storage backend error writing the root key, permission-denied on the storage directory, or a root cert in storage without a parseable key. The handler constructs ca = new(CA) and calls Provision with the admin context.

Common situations: First request to the admin PKI endpoint on a fresh install whose storage dir is root-owned or read-only; custom storage module (e.g. consul/redis) unreachable at that moment; storage resources half-written after a crash; running with an old root key file format.

Related errors


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