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 errView on GitHub (pinned to 50e54ee279)
Solutions
- Use exactly GET /pki/ca/<id> for CA metadata or GET /pki/ca/<id>/certificates for the PEM chain
- Use the default CA id `local`: GET /pki/ca/local
- Check the echoed path in the error response body to spot the typo
- 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
- Centralize admin API path construction in one helper
- Avoid trailing slashes after the CA id
- Pin automation against one Caddy version's admin API docs
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
- no certificate authority configured with id: %s
- request path is missing object ID
- malformed object path
- [%s] key does not exist: %s
- method not allowed: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/c75ae5e9035d5142.
Report an issue: GitHub.