ory/hydra · error

error parsing patch operations: %v

Error message

error parsing patch operations: %v

What it means

After allow-listing the operation kind, ApplyJSONPatch calls op.Path() on each decoded JSON Patch operation. If the operation object is malformed — most commonly a 'move' or 'copy' operation that carries 'from' but no 'path', or a non-string/missing 'path' field — evanphx/json-patch fails to extract the path and this error wraps that failure.

Source

Thrown at oryx/jsonx/patch.go:69

	patch, err := jsonpatch.DecodePatch(p)
	if err != nil {
		return result, errors.WithStack(err)
	}

	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()

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Ensure every operation in the patch document includes a string "path" member per RFC 6902.
  2. Validate the patch client-side with a JSON Patch schema before sending.
  3. Replace move/copy operations (which often lack 'path' handling here) with explicit add/remove operations.
  4. Check that the request body is not double-encoded: send the patch array directly, not as a JSON string.

Example fix

// before
[{"op": "remove"}]
// after
[{"op": "remove", "path": "/name"}]
Defensive patterns

Strategy: validation

Validate before calling

for _, op := range patchOps {
    p, ok := op["path"].(string)
    if !ok || p == "" {
        return fmt.Errorf("every patch operation needs a string 'path'")
    }
}

Prevention

When it happens

Trigger: Calling ApplyJSONPatch with an operation entry missing the required "path" member or with a non-string path value, e.g. [{"op":"remove"}] or {"op":"add","path":123}. Reached via patchOAuth2Client with a hand-crafted PATCH body.

Common situations: Buggy client-side patch builders, serializers that drop empty strings, or move/copy operations that only set 'from'. Also seen when a patch is double-encoded JSON or manually assembled incorrectly.

Related errors


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