Skip to content

replace rejects an object member named "-", so make_patch can produce patches that fail to apply #212

Closed

Description

@stefankoegl

replace raises InvalidJsonPatch for every path whose last token is -, including when - names an object member rather than the end of an array. make_patch emits replace for a changed member, so it can produce patches that apply then rejects:

>>> import jsonpatch
>>> patch = jsonpatch.make_patch({'-': 0}, {'-': 1})
>>> patch.patch
[{'op': 'replace', 'path': '/-', 'value': 1}]
>>> patch.apply({'-': 0})
jsonpatch.InvalidJsonPatch: 'path' with '-' can't be applied to 'replace' operation

test and remove handle the same path correctly; remove of /- turns {'-': 0} into {}. RFC 6901 gives - a special meaning only when the referenced value is an array. For an object it is an ordinary member name. Rejecting /- in replace on an array is correct, because it refers to a nonexistent element.

Cause: ReplaceOperation.apply checks part == "-" before looking at the type of subobj.

Suggested fix: only reject - when subobj is a sequence.

Tests: these tests in property_tests.py are marked @unittest.expectedFailure because of this bug; drop the markers once it is fixed:

  • OperationProperties.test_replace_any_existing_location
  • MakePatchProperties.test_roundtrip

On current master (e0e7703) this is the only failure Hypothesis finds for test_roundtrip. With - keys excluded, the roundtrip passed 80,000 generated document pairs across 4 hash seeds.

Activity

  1. stefankoegl commented on Oct 10, 2026

    @stefankoegl
    OwnerAuthor

    Fixed by 12574b5 ("Only reject '-' in replace when it refers to an array"), merged in #223.

    On current master (077b4f4), make_patch({'-': 0}, {'-': 1}).apply({'-': 0}) returns {'-': 1}, and replace of /- on an array is still rejected. test_replace_any_existing_location and test_roundtrip in property_tests.py no longer carry expectedFailure and pass, at 5,000 examples on two hash seeds as well.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions