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
- Verify the root is a map (configuration.jsonProvider().isMap(root)) before putting.
- Put the key at the correct nested path, e.g. JsonPath.put(document, "$.settings", key, value, conf).
- If the root is an array, address a specific element: "$." + index or "$[i]" then put there.
- 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
- Confirm the root is a JSON object before key-based mutations
- Put at the correct nested path (e.g. $.meta) rather than "$"
- Handle endpoints that return top-level arrays by wrapping or addressing elements
- Pin response schemas with contract tests to catch shape drift early
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
- No results for Key %s found in map!
- Invalid set operation
- Invalid map operation
- Invalid delete operation
- Invalid add operation. $ is not an array
AI-assisted analysis of json-path/JsonPath@62a4c9f0f6 (2026-09-11).
Data as JSON: /api/errors/9c24b739f2d2973e.
Report an issue: GitHub.