Grasscutters/Grasscutter · error · IOException
Invalid field in Position definition -
Error message
Invalid field in Position definition -
What it means
Gson TypeAdapter for emu.grasscutter.utils.Position reads a JSON object and only accepts the keys x/X/_x, y/Y/_y, z/Z/_z. Any other key inside the object form causes this IOException. It exists because Position is serialized as an array but deserialized leniently from object form, so unknown fields are treated as a definition error rather than ignored.
Source
Thrown at src/main/java/emu/grasscutter/utils/JsonAdapters.java:136
case BEGIN_ARRAY -> { // "pos": [x,y,z]
reader.beginArray();
val array = new FloatArrayList(3);
while (reader.hasNext()) array.add(reader.nextInt());
reader.endArray();
return new Position(array);
}
case BEGIN_OBJECT -> { // "pos": {"x": x, "y": y, "z": z}
float x = 0f;
float y = 0f;
float z = 0f;
reader.beginObject();
for (var next = reader.peek(); next != JsonToken.END_OBJECT; next = reader.peek()) {
val name = reader.nextName();
switch (name) {
case "x", "X", "_x" -> x = (float) reader.nextDouble();
case "y", "Y", "_y" -> y = (float) reader.nextDouble();
case "z", "Z", "_z" -> z = (float) reader.nextDouble();
default -> throw new IOException("Invalid field in Position definition - " + name);
}
}
reader.endObject();
return new Position(x, y, z);
}
default -> throw new IOException("Invalid Position definition - " + reader.peek().name());
}
}
@Override
public void write(JsonWriter writer, Position i) throws IOException {
writer.beginArray();
writer.value(i.getX());
writer.value(i.getY());
writer.value(i.getZ());
writer.endArray();
}
}View on GitHub (pinned to f373827a83)
Solutions
- Remove or rename the offending key in the JSON object so only x/X/_x, y/Y/_y, z/Z/_z remain
- Switch the JSON value to the canonical array form [x, y, z] which bypasses the key switch entirely
- If extra fields are expected, preprocess the JSON to strip unknown keys before parsing, or extend the adapter's switch to ignore unknown names
Example fix
// before
"pos": {"x": 100, "y": 200, "z": 300, "rot": 45}
// after
"pos": {"x": 100, "y": 200, "z": 300} Defensive patterns
Strategy: validation
Validate before calling
boolean isValidPositionObject(com.google.gson.JsonObject o) {
if (o == null) return false;
return o.keySet().stream().allMatch(k -> k.matches("[xyzXYZ]|_[xyz]"));
} Type guard
boolean isValidPosition(Object v) {
return v instanceof com.google.gson.JsonElement e && e.isJsonObject()
&& isValidPositionObject(e.getAsJsonObject());
} Try / catch
try {
Position p = gson.fromJson(json, Position.class);
} catch (IOException e) {
logger.error("Bad Position JSON: {}", e.getMessage());
} Prevention
- Only use keys x/X/_x, y/Y/_y, z/Z/_z in object-form positions
- Prefer the canonical array form [x,y,z] in configs
- Lint JSON files against the Position schema before loading
- Strip unknown keys when importing data from external tools
When it happens
Trigger: Deserializing JSON into Position (directly via Gson registered with JsonAdapters, or via any config/data class containing Position fields) where the value is an object containing a key other than x/X/_x/y/Y/_y/z/Z/_z, e.g. {"x":1,"z":2,"w":3} or a nested typo like {"pos_x":1}.
Common situations: Hand-edited spawn/scene/config JSON with extra or misspelled coordinates; importing data dumps from other tools that emit wrapped or extended position objects; schema drift after a game-data format change.
Related errors
- Invalid DynamicFloat definition -
- Invalid IntList definition -
- Invalid GridPosition definition -
- Invalid Position definition -
- Invalid Enum definition -
AI-assisted analysis of Grasscutters/Grasscutter@f373827a83 (2026-09-03).
Data as JSON: /api/errors/b40f2f06b2e5393c.
Report an issue: GitHub.