caddyserver/caddy · info · caddy.APIError

resource not found: %v

Error message

resource not found: %v

What it means

A 404 caddy.APIError returned by the PKI admin API router when a request under /pki/ does not match the two supported shapes: GET /pki/ca/<id> (CA info) or GET /pki/ca/<id>/certificates (PEM chain). Any other path — wrong segment count, unknown sub-resource, or missing id — lands in the default branch. The error echoes r.URL.Path so the caller sees exactly what was unmatched.

Source

Thrown at modules/caddypki/adminapi.go:86

			Pattern: adminPKIEndpointBase,
			Handler: caddy.AdminHandlerFunc(a.handleAPIEndpoints),
		},
	}
}

// handleAPIEndpoints routes API requests within adminPKIEndpointBase.
func (a *adminAPI) handleAPIEndpoints(w http.ResponseWriter, r *http.Request) error {
	uri := strings.TrimPrefix(r.URL.Path, "/pki/")
	parts := strings.Split(uri, "/")
	switch {
	case len(parts) == 2 && parts[0] == "ca" && parts[1] != "":
		return a.handleCAInfo(w, r)
	case len(parts) == 3 && parts[0] == "ca" && parts[1] != "" && parts[2] == "certificates":
		return a.handleCACerts(w, r)
	}
	return caddy.APIError{
		HTTPStatus: http.StatusNotFound,
		Err:        fmt.Errorf("resource not found: %v", r.URL.Path),
	}
}

// handleCAInfo returns information about a particular
// CA by its ID. If the CA ID is the default, then the CA will be
// provisioned if it has not already been. Other CA IDs will return an
// error if they have not been previously provisioned.
func (a *adminAPI) handleCAInfo(w http.ResponseWriter, r *http.Request) error {
	if r.Method != http.MethodGet {
		return caddy.APIError{
			HTTPStatus: http.StatusMethodNotAllowed,
			Err:        fmt.Errorf("method not allowed: %v", r.Method),
		}
	}

	ca, err := a.getCAFromAPIRequestPath(r)
	if err != nil {
		return err

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Use exactly GET /pki/ca/<id> for CA metadata or GET /pki/ca/<id>/certificates for the PEM chain
  2. Use the default CA id `local`: GET /pki/ca/local
  3. Check the echoed path in the error response body to spot the typo
  4. Confirm the admin endpoint base (default localhost:2019) is the one being addressed

Example fix

# before
curl http://localhost:2019/pki/ca/local/chain

# after
curl http://localhost:2019/pki/ca/local/certificates
Defensive patterns

Strategy: validation

Validate before calling

// Build only supported PKI admin paths:
func pkiPath(id string, certs bool) (string, error) {
    if id == "" { return "", errors.New("empty CA id") }
    if certs { return "/pki/ca/" + id + "/certificates", nil }
    return "/pki/ca/" + id, nil
}

Try / catch

// Treat 404 from the PKI API as a path bug, not a transient error:
resp, err := http.Get(base + "/pki/ca/" + id)
if err != nil { return err }
if resp.StatusCode == 404 {
    return fmt.Errorf("unsupported PKI API path; use /pki/ca/<id> or /pki/ca/<id>/certificates")
}

Prevention

When it happens

Trigger: Calling GET /pki/ca, /pki/ca/, /pki/cas/local, /pki/ca/local/chain, or POST /pki/ca/local (path is fine but a later method check is separate — for the router itself, any unmatched path). Reaching this error means the path itself is wrong, independent of HTTP method.

Common situations: Scripts written against another ACME/CA admin API assuming /certificates lives at a different path; trailing-slash differences (`/pki/ca/local/` has 3 trailing segments and 404s); typos in automation curl commands.

Related errors


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