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

  1. Remove the denied field(s) from the patch and only patch allowed paths.
  2. 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).
  3. Check the endpoint's documented denied paths and validate your patch paths against the same globs client-side before sending.
  4. 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

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


AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03). Data as JSON: /api/errors/2cfce035149a1ab4. Report an issue: GitHub.