{"record":{"id":"855f001d803fe5d2","repo":"HMCL-dev/HMCL","slug":"account-private-data-is-not-an-object","errorCode":null,"errorMessage":"Account private data is not an object","messagePattern":"Account private data is not an object","errorType":"validation","errorClass":"JsonParseException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/setting/AccountPrivateData.java","lineNumber":272,"sourceCode":"                JsonSerializationContext context) {\n            JsonObject result = new JsonObject();\n            result.addProperty(JsonSchema.PROPERTY_SCHEMA, accountPrivateData.getSchema().url());\n            protectionMode().writePayload(result, createPayload(accountPrivateData, context));\n            accountPrivateData.unknownFields.forEach(result::add);\n            return result;\n        }\n\n        /// Deserializes the private data store from a protected payload envelope.\n        @Override\n        public @Nullable AccountPrivateData deserialize(\n                @Nullable JsonElement json,\n                Type typeOfT,\n                JsonDeserializationContext context) throws JsonParseException {\n            if (json == null || json.isJsonNull()) {\n                return null;\n            }\n            if (!(json instanceof JsonObject object)) {\n                throw new JsonParseException(\"Account private data is not an object\");\n            }\n\n            AccountPrivateData accountPrivateData = new AccountPrivateData();\n            Map<String, JsonElement> values = new LinkedHashMap<>(object.asMap());\n            JsonElement schema = values.remove(JsonSchema.PROPERTY_SCHEMA);\n            if (schema != null && schema.isJsonPrimitive() && schema.getAsJsonPrimitive().isString()) {\n                accountPrivateData.setSchema(new JsonSchema(schema.getAsString()));\n            }\n            values.remove(ProtectedPayload.PROPERTY_PROTECTION);\n            values.remove(ProtectedPayload.PROPERTY_NONCE);\n            values.remove(ProtectedPayload.PROPERTY_PAYLOAD);\n            accountPrivateData.unknownFields.putAll(values);\n\n            readPayload(accountPrivateData, ProtectedPayload.read(object, JsonArray.class), context);\n            return accountPrivateData;\n        }\n    }\n}","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/setting/AccountPrivateData.java#L254-L290","documentation":"AccountPrivateData's Gson deserializer accepts JSON null (returns null) but requires that any present value be a JSON object. If the element is an array, string, or primitive, it throws JsonParseException(\"Account private data is not an object\"). This protects the migration/upgrade logic that iterates the object's members and applies schema upgrades.","triggerScenarios":"Deserializing the accounts config when an account entry (or the whole accounts value) is a non-object JSON value — e.g. an old-format array of accounts, an account serialized as a string, or a corrupted config file.","commonSituations":"Hand-edited hmcl accounts.json with malformed structure; config written by a much older HMCL version using a different layout; file truncated so a fragment (array/string) remains; manual merge of config files gone wrong.","solutions":["Open the config file and fix the account entry to be a JSON object (or delete the malformed entry so HMCL recreates it)","Back up and remove the corrupted accounts config, then re-add the account in the UI","If migrating from an old format, use the HMCL version that supports that format to convert first","Wrap deserialization in try-catch for JsonParseException and fall back to an empty/legacy migration path"],"exampleFix":"// before\n\"accounts\": [{ \"username\": \"steve\" }]\n// after\n\"accounts\": { \"0\": { \"username\": \"steve\" } }","handlingStrategy":"validation","validationCode":"JsonElement el = config.get(\"accounts\");\nif (el != null && !el.isJsonNull() && !(el instanceof JsonObject))\n    throw new IOException(\"accounts config must be a JSON object\");","typeGuard":"static boolean isAccountObject(JsonElement el) {\n    return el != null && el instanceof JsonObject;\n}","tryCatchPattern":"try {\n    AccountPrivateData d = gson.fromJson(el, AccountPrivateData.class);\n} catch (JsonParseException e) {\n    LOGGER.warning(\"Malformed account entry, skipping: \" + e.getMessage());\n    // keep other accounts, re-create the malformed one\n}","preventionTips":["Never hand-edit the accounts config while HMCL is running","Back up the config before manual edits or migrations","Migrate old formats through the HMCL version that supports them","Validate JSON structure with a schema check before distributing configs"],"tags":["json","deserialization","accounts","config"],"backgroundTag":"config-type-mismatch","analyzedSha":"24702dc5a0214034f4c27166d5fd30cad08cec19","analyzedAt":"2026-09-10T12:36:46.680Z","contentChangedAt":"2026-09-10T12:36:46.680Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}