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

  1. Remove or rename the offending key in the JSON object so only x/X/_x, y/Y/_y, z/Z/_z remain
  2. Switch the JSON value to the canonical array form [x, y, z] which bypasses the key switch entirely
  3. 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

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


AI-assisted analysis of Grasscutters/Grasscutter@f373827a83 (2026-09-03). Data as JSON: /api/errors/b40f2f06b2e5393c. Report an issue: GitHub.