json-path/JsonPath · error · InvalidModificationException

Invalid put operation. $ is not a map

Error message

Invalid put operation. $ is not a map

What it means

The root ('$') PathRef.put sets a key/value pair on the root only if the root is a JSON map. When the root is an array or scalar, put is impossible, so it throws InvalidModificationException 'Invalid put operation. $ is not a map'.

Source

Thrown at json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java:136

        public void delete(Configuration configuration) {
            throw new InvalidModificationException("Invalid delete operation");
        }

        @Override
        public void add(Object newVal, Configuration configuration) {
            if(configuration.jsonProvider().isArray(parent)){
                configuration.jsonProvider().setArrayIndex(parent, configuration.jsonProvider().length(parent), newVal);
            } else {
                throw new InvalidModificationException("Invalid add operation. $ is not an array");
            }
        }

        @Override
        public void put(String key, Object newVal, Configuration configuration) {
            if(configuration.jsonProvider().isMap(parent)){
                configuration.jsonProvider().setProperty(parent, key, newVal);
            } else {
                throw new InvalidModificationException("Invalid put operation. $ is not a map");
            }
        }

        @Override
        public void renameKey(String oldKeyName, String newKeyName, Configuration configuration) {
            Object target = parent;
            if(targetInvalid(target)){
                return;
            }
            renameInMap(target, oldKeyName, newKeyName, configuration);
        }

    }

    private static class ArrayIndexPathRef extends PathRef {

        private int index;

View on GitHub (pinned to 62a4c9f0f6)

Solutions

  1. Verify the root is a map (configuration.jsonProvider().isMap(root)) before putting.
  2. Put the key at the correct nested path, e.g. JsonPath.put(document, "$.settings", key, value, conf).
  3. If the root is an array, address a specific element: "$." + index or "$[i]" then put there.
  4. Normalize/wrap array-root documents into an object before applying key-based mutations.

Example fix

// before
JsonPath.put(document, "$", "version", 2, conf); // root is an array
// after
JsonPath.put(document, "$.meta", "version", 2, conf);
Defensive patterns

Strategy: type-guard

Validate before calling

Object root = JsonPath.parse(document).json();
if (!(root instanceof Map)) {
    throw new IllegalArgumentException("Root is not a map; put keys on a nested object path instead");
}

Type guard

boolean rootIsMap(Object doc, Configuration conf) {
    Object root = JsonPath.parse(doc, conf).json();
    return conf.jsonProvider().isMap(root);
}

Try / catch

try {
    JsonPath.put(document, "$", key, newVal, conf);
} catch (InvalidModificationException e) {
    throw new IllegalStateException("Root of document is not a JSON object; cannot put '" + key + "'", e);
}

Prevention

When it happens

Trigger: Calling JsonPath.put(document, "$", key, newVal, configuration) where the parsed root is an array (e.g. a JSON array document) or any non-object value.

Common situations: Documents returned as top-level JSON arrays while code assumes an object root; putting a field at the wrong level — the target object is a nested property, not the root; endpoints whose payload shape changed between versions.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of json-path/JsonPath@62a4c9f0f6 (2026-09-11). Data as JSON: /api/errors/9c24b739f2d2973e. Report an issue: GitHub.