ory/hydra · error
patch includes denied path: %s
Error message
patch includes denied path: %s
What it means
ApplyJSONPatch accepts denyPaths globs (compiled with gobwas/glob) and rejects any patch operation whose target path matches a denied pattern. This lets API surfaces (like patchOAuth2Client) protect immutable fields from modification. The error reports the offending path verbatim.
Source
Thrown at oryx/jsonx/patch.go:72
}
denyPattern := fmt.Sprintf("{%s}", strings.ToLower(strings.Join(denyPaths, ",")))
matcher, err := glob.Compile(denyPattern, '/')
if err != nil {
return result, errors.WithStack(err)
}
for _, op := range patch {
// Some operations are buggy, see https://github.com/evanphx/json-patch/pull/158
if isUnsupported(op) {
return result, errors.Errorf("unsupported operation: %s", op.Kind())
}
path, err := op.Path()
if err != nil {
return result, errors.Errorf("error parsing patch operations: %v", err)
}
if matcher.Match(strings.ToLower(path)) {
return result, errors.Errorf("patch includes denied path: %s", path)
}
// JSON patch officially rejects replacing paths that don't exist, but we want to be more tolerant.
// Therefore, we will ensure that all paths that we want to replace exist in the original document.
if op.Kind() == "replace" && !isElementAccess(path) {
op["op"] = new(json.RawMessage(`"add"`))
}
}
original, err := json.Marshal(object)
if err != nil {
return result, errors.WithStack(err)
}
options := jsonpatch.NewApplyOptions()
options.EnsurePathExistsOnAdd = true
modified, err := patch.ApplyWithOptions(original, options)View on GitHub (pinned to 4174065ffb)
Solutions
- Remove the denied field(s) from the patch and only patch allowed paths.
- If the field must change, use the dedicated API for that change (e.g., replace the whole resource with PUT, or a specific endpoint for credentials).
- Check the endpoint's documented denied paths and validate your patch paths against the same globs client-side before sending.
- Verify glob case sensitivity: matching is done on the lowercased path, so pattern expectations should be lowercase too.
Example fix
// before
[{"op": "replace", "path": "/secret", "value": "x"}] // denied path
// after
[{"op": "replace", "path": "/clientName", "value": "New Name"}] Defensive patterns
Strategy: validation
Validate before calling
// Mirror the server's deny list client-side
var deny = glob.MustCompile("{/secret,/credentials}", '/')
for _, op := range patchOps {
if deny.Match(strings.ToLower(op["path"].(string))) {
return fmt.Errorf("path %v is not patchable", op["path"])
}
} Prevention
- Know the endpoint's denied paths and skip them in patch builders.
- Use dedicated APIs (PUT/credentials endpoint) to change protected fields.
- Match paths case-insensitively since the server lowercases them.
When it happens
Trigger: Calling ApplyJSONPatch (oryx/jsonx/patch.go:72) with a patch operation whose "path" matches one of the denyPaths globs passed by the caller (e.g., attempting to PATCH a protected OAuth2 client field).
Common situations: Trying to modify read-only or server-managed fields through a PATCH endpoint — e.g., changing an OAuth2 client's token endpoint auth method, credentials, or other denied properties.
Related errors
- unsupported operation: %s
- Token is expired
- Token used before issued
- Token is not valid yet
- cookiex: purpose must be non-empty and must not contain a pi
AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03).
Data as JSON: /api/errors/2cfce035149a1ab4.
Report an issue: GitHub.