{"record":{"id":"72da99c95a6f1713","repo":"Grasscutters/Grasscutter","slug":"invalid-dynamicfloat-definition","errorCode":null,"errorMessage":"Invalid DynamicFloat definition - ","messagePattern":"Invalid DynamicFloat definition - ","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"src/main/java/emu/grasscutter/utils/JsonAdapters.java","lineNumber":38,"sourceCode":"                case STRING -> {\n                    return new DynamicFloat(reader.nextString());\n                }\n                case NUMBER -> {\n                    return new DynamicFloat((float) reader.nextDouble());\n                }\n                case BOOLEAN -> {\n                    return new DynamicFloat(reader.nextBoolean());\n                }\n                case BEGIN_ARRAY -> {\n                    reader.beginArray();\n                    val opStack = new ArrayList<DynamicFloat.StackOp>();\n                    while (reader.hasNext()) {\n                        opStack.add(\n                                switch (reader.peek()) {\n                                    case STRING -> new DynamicFloat.StackOp(reader.nextString());\n                                    case NUMBER -> new DynamicFloat.StackOp((float) reader.nextDouble());\n                                    case BOOLEAN -> new DynamicFloat.StackOp(reader.nextBoolean());\n                                    default -> throw new IOException(\n                                            \"Invalid DynamicFloat definition - \" + reader.peek().name());\n                                });\n                    }\n                    reader.endArray();\n                    return new DynamicFloat(opStack);\n                }\n                default -> throw new IOException(\n                        \"Invalid DynamicFloat definition - \" + reader.peek().name());\n            }\n        }\n\n        @Override\n        public void write(JsonWriter writer, DynamicFloat f) {}\n    }\n\n    class IntListAdapter extends TypeAdapter<IntList> {\n        @Override\n        public IntList read(JsonReader reader) throws IOException {","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/Grasscutters/Grasscutter/blob/f373827a83d3e688fd3c6afb1e1c3759ca1d61c9/src/main/java/emu/grasscutter/utils/JsonAdapters.java#L20-L56","documentation":"Grasscutter's DynamicFloat Gson adapter expects a JSON array whose elements are only strings, numbers, or booleans (stack ops). When the next JSON token is anything else (e.g. OBJECT, NULL, or a non-array value), the adapter throws this IOException with the offending token name appended. Here the token name is empty in the message, indicating the peek value was unexpected during parsing of a resource file.","triggerScenarios":"Parsing a resource/quest JSON file whose DynamicFloat field contains a nested JSON object, null, or a non-array scalar (e.g. a bare number or string where an array of stack ops was expected) while loading game resources via Gson with this TypeAdapter registered.","commonSituations":"Game data files from a mismatched resources version (dumped data newer/older than the parser expects), hand-edited JSON that replaced the array with an object or null, or a corrupted/truncated download where the array content is malformed.","solutions":["Open the resource JSON named in the stack trace and fix the DynamicFloat field to be an array of strings/numbers/booleans","Replace null or nested objects in the field with a valid array, e.g. [\"%HP\"] or [100.0]","Re-download or re-dump the resources from a source matching your Grasscutter version","Check the JsonAdapters DynamicFloat adapter version matches the resource data format"],"exampleFix":"// before (resource JSON)\n\"battle_progress\": {\"value\": 100}\n// after\n\"battle_progress\": [100.0]","handlingStrategy":"validation","validationCode":"// Pre-validate a DynamicFloat resource value before handing it to Gson\nstatic boolean isValidDynamicFloat(com.google.gson.JsonElement e) {\n    if (e == null || e.isJsonNull()) return false;\n    if (e.isJsonPrimitive()) {\n        var p = e.getAsJsonPrimitive();\n        return p.isString() || p.isNumber() || p.isBoolean();\n    }\n    if (e.isJsonArray()) {\n        for (var el : e.getAsJsonArray()) {\n            if (!el.isJsonPrimitive()) return false;\n            var p = el.getAsJsonPrimitive();\n            if (!(p.isString() || p.isNumber() || p.isBoolean())) return false;\n        }\n        return true;\n    }\n    return false; // objects and other tokens are rejected by the adapter\n}","typeGuard":"static boolean isDynamicFloatPrimitive(com.google.gson.JsonElement el) {\n    return el != null && el.isJsonPrimitive()\n        && (el.getAsJsonPrimitive().isString()\n            || el.getAsJsonPrimitive().isNumber()\n            || el.getAsJsonPrimitive().isBoolean());\n}","tryCatchPattern":"try {\n    DynamicFloat f = gson.fromJson(json, DynamicFloat.class);\n} catch (IOException | JsonParseException e) {\n    logger.error(\"Malformed DynamicFloat in resource JSON: {}\", e.getMessage());\n    DynamicFloat f = new DynamicFloat(0f); // safe default\n}","preventionTips":["Only use resource dumps that match your Grasscutter version","Never hand-edit DynamicFloat fields without keeping the array-of-scalars shape","Lint resource JSONs at load time with the validation function before Gson deserialization","Validate resource files after download (checksum/schema check)"],"tags":["gson","json","deserialization","grasscutter"],"backgroundTag":"json-deserialization-failed","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"}