caddyserver/caddy · error · APIError
[%s] key does not exist: %s
Error message
[%s] key does not exist: %s
What it means
Returned (APIError, HTTP 404) when PATCH targets a map key that does not exist. PATCH is strictly update-only: the final path segment must already be present in the config map. There is no implicit creation with PATCH.
Source
Thrown at admin.go:1293
} else {
v[part] = append(arr, val)
}
} else {
v[part] = val
}
case http.MethodPut:
if _, ok := v[part]; ok {
return APIError{
HTTPStatus: http.StatusConflict,
Err: fmt.Errorf("[%s] key already exists: %s", path, part),
}
}
v[part] = val
case http.MethodPatch:
if _, ok := v[part]; !ok {
return APIError{
HTTPStatus: http.StatusNotFound,
Err: fmt.Errorf("[%s] key does not exist: %s", path, part),
}
}
v[part] = val
case http.MethodDelete:
if _, ok := v[part]; !ok {
return APIError{
HTTPStatus: http.StatusNotFound,
Err: fmt.Errorf("[%s] key does not exist: %s", path, part),
}
}
delete(v, part)
default:
return fmt.Errorf("unrecognized method %s", method)
}
} else {
// if we are "PUTting" a new resource, the key(s) in its path
// might not exist yet; that's OK but we need to make them as
// we go, while we still have a pointer from the level aboveView on GitHub (pinned to 50e54ee279)
Solutions
- Create first with PUT (key must not exist), then PATCH
- GET the parent path to confirm the key exists before patching
- If your workflow replaced the config, re-apply the full desired state instead of patching stale paths
Example fix
# before curl -X PATCH http://localhost:2019/config/apps/http/servers/myserver3/listen -d '[":8443"]' # 404 # after curl -X PUT http://localhost:2019/config/apps/http/servers/myserver3/listen -d '[":8443"]'
Defensive patterns
Strategy: validation
Validate before calling
key="myserver3" exists=$(curl -s "http://localhost:2019/config/apps/http/servers" | jq --arg k "$key" 'has($k)') [ "$exists" = "true" ] && curl -X PATCH "http://localhost:2019/config/apps/http/servers/$key" -d @body.json \ || curl -X PUT "http://localhost:2019/config/apps/http/servers/$key" -d @body.json
Try / catch
On 404 from PATCH, verify the key against a fresh GET of the parent; create with PUT if it should exist, otherwise fix the stale path.
Prevention
- PATCH never creates; ensure creation happened first
- Re-resolve paths after config reloads or full-config replacements
When it happens
Trigger: PATCH /config/apps/http/servers/myserver3/listen when 'myserver3' was never created, or patching a key that was deleted by a concurrent client or a config reload that reset the tree.
Common situations: Assuming PATCH upserts (RFC-style) when Caddy defines it as update-only; patching after another process replaced the whole config via PUT /config/ or a Caddyfile reload, dropping the key.
Related errors
- unknown object ID '%s'
- decoding request body: %w, at offset %d
- decoding request body: %w
- no traversable path
- path missing
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/49aff338a94a0447.
Report an issue: GitHub.