{"record":{"id":"b40f2f06b2e5393c","repo":"Grasscutters/Grasscutter","slug":"invalid-field-in-position-definition","errorCode":null,"errorMessage":"Invalid field in Position definition - ","messagePattern":"Invalid field in Position definition - ","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"src/main/java/emu/grasscutter/utils/JsonAdapters.java","lineNumber":136,"sourceCode":"                case BEGIN_ARRAY -> { // \"pos\": [x,y,z]\n                    reader.beginArray();\n                    val array = new FloatArrayList(3);\n                    while (reader.hasNext()) array.add(reader.nextInt());\n                    reader.endArray();\n                    return new Position(array);\n                }\n                case BEGIN_OBJECT -> { // \"pos\": {\"x\": x, \"y\": y, \"z\": z}\n                    float x = 0f;\n                    float y = 0f;\n                    float z = 0f;\n                    reader.beginObject();\n                    for (var next = reader.peek(); next != JsonToken.END_OBJECT; next = reader.peek()) {\n                        val name = reader.nextName();\n                        switch (name) {\n                            case \"x\", \"X\", \"_x\" -> x = (float) reader.nextDouble();\n                            case \"y\", \"Y\", \"_y\" -> y = (float) reader.nextDouble();\n                            case \"z\", \"Z\", \"_z\" -> z = (float) reader.nextDouble();\n                            default -> throw new IOException(\"Invalid field in Position definition - \" + name);\n                        }\n                    }\n                    reader.endObject();\n                    return new Position(x, y, z);\n                }\n                default -> throw new IOException(\"Invalid Position definition - \" + reader.peek().name());\n            }\n        }\n\n        @Override\n        public void write(JsonWriter writer, Position i) throws IOException {\n            writer.beginArray();\n            writer.value(i.getX());\n            writer.value(i.getY());\n            writer.value(i.getZ());\n            writer.endArray();\n        }\n    }","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/Grasscutters/Grasscutter/blob/f373827a83d3e688fd3c6afb1e1c3759ca1d61c9/src/main/java/emu/grasscutter/utils/JsonAdapters.java#L118-L154","documentation":"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.","triggerScenarios":"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}.","commonSituations":"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.","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"],"exampleFix":"// before\n\"pos\": {\"x\": 100, \"y\": 200, \"z\": 300, \"rot\": 45}\n// after\n\"pos\": {\"x\": 100, \"y\": 200, \"z\": 300}","handlingStrategy":"validation","validationCode":"boolean isValidPositionObject(com.google.gson.JsonObject o) {\n  if (o == null) return false;\n  return o.keySet().stream().allMatch(k -> k.matches(\"[xyzXYZ]|_[xyz]\"));\n}","typeGuard":"boolean isValidPosition(Object v) {\n  return v instanceof com.google.gson.JsonElement e && e.isJsonObject()\n      && isValidPositionObject(e.getAsJsonObject());\n}","tryCatchPattern":"try {\n  Position p = gson.fromJson(json, Position.class);\n} catch (IOException e) {\n  logger.error(\"Bad Position JSON: {}\", e.getMessage());\n}","preventionTips":["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"],"tags":["json","gson","deserialization","grasscutter"],"backgroundTag":"invalid-json-field","analyzedSha":"f373827a83d3e688fd3c6afb1e1c3759ca1d61c9","analyzedAt":"2026-09-03T22:43:49.407Z","contentChangedAt":"2026-09-03T22:43:49.407Z","schemaVersion":2},"datasetVersion":"2026-09-11T07:07:21.782Z"}