{"record":{"id":"1531944a39841166","repo":"HMCL-dev/HMCL","slug":"missing-payload","errorCode":null,"errorMessage":"Missing payload","messagePattern":"Missing payload","errorType":"validation","errorClass":"JsonParseException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/setting/ProtectedPayload.java","lineNumber":76,"sourceCode":"\n    /// Selects how a protected payload is stored in its JSON envelope.\n    @NotNullByDefault\n    enum ProtectionMode {\n        /// Stores the payload as plain JSON for development and diagnostics.\n        PLAIN(\"plain\") {\n            /// Writes the payload into the given envelope.\n            @Override\n            protected void writePayload(JsonObject envelope, JsonElement payload) {\n                envelope.addProperty(PROPERTY_PROTECTION, id());\n                envelope.add(PROPERTY_PAYLOAD, payload);\n            }\n\n            /// Reads the payload from the given envelope.\n            @Override\n            protected JsonElement readPayload(JsonObject envelope) {\n                JsonElement payload = envelope.get(PROPERTY_PAYLOAD);\n                if (payload == null) {\n                    throw new JsonParseException(\"Missing payload\");\n                }\n                return payload;\n            }\n        },\n\n        /// Stores the payload as a portable weakly encrypted envelope.\n        ///\n        /// The payload is encrypted with a built-in application key, Base64-encoded, and split into padded strings. This\n        /// avoids storing private data as directly readable JSON, but it should not be treated as device-bound secret\n        /// storage. The nonce is stored separately, while the payload lanes store `ciphertext || authentication tag`.\n        OBFUSCATED_V1(\"hmcl-obfuscated-v1\") {\n            /// The number of lanes used by the obfuscated payload.\n            private static final int OBFUSCATED_LANE_COUNT = 4;\n\n            /// The number of padding elements stored before each lane.\n            private static final int LANE_PADDING_COUNT = 63;\n\n            /// The number of elements written in an obfuscated payload array.","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/setting/ProtectedPayload.java#L58-L94","documentation":"ProtectionMode.PLAIN.readPayload throws this when a JSON envelope marked 'protection: plain' contains no 'payload' member. The PLAIN mode expects the payload stored directly as a JSON element; without it the envelope is malformed/incomplete, so the library refuses to guess and raises JsonParseException during read().","triggerScenarios":"Calling ProtectedPayload.read(envelope, type) on an envelope whose protection marker is 'plain' but which lacks the 'payload' member — e.g. a hand-edited settings file, a partially written file, or code that writes the protection marker without calling writePayload.","commonSituations":"Manually editing or trimming HMCL config files and deleting the payload member; merging config files and dropping fields; an older/newer version writing a different envelope shape; truncated file writes leaving only the protection key.","solutions":["Restore the 'payload' member in the envelope (re-generate it by re-saving the setting through HMCL).","Verify the file was not truncated or hand-edited; re-download or reset the config file.","If you build envelopes yourself, always write via ProtectionMode.writePayload so 'payload' and 'protection' are set together."],"exampleFix":"// before: hand-crafted envelope\n{\"protection\": \"plain\"}\n// after: envelope with payload present\n{\"protection\": \"plain\", \"payload\": {\"accounts\": []}}","handlingStrategy":"validation","validationCode":"if (envelope.has(\"payload\")) {\n    ProtectedPayload.read(envelope, JsonElement.class);\n} else {\n    // fall back to defaults or recreate the setting\n}","typeGuard":"static boolean hasPlainPayload(com.google.gson.JsonObject envelope) {\n    return envelope.has(\"payload\") && !envelope.get(\"payload\").isJsonNull();\n}","tryCatchPattern":"try {\n    JsonElement payload = ProtectedPayload.read(envelope, JsonObject.class);\n} catch (com.google.gson.JsonParseException e) {\n    logger.warning(\"Envelope missing payload, using defaults\", e);\n    return defaults();\n}","preventionTips":["Never hand-edit envelope JSON; let HMCL write it.","Always pair the protection marker with its payload member via writePayload.","Validate envelope files after automated merges or sync tools touch them.","Keep backups of config files before manual changes."],"tags":["json","deserialization","config-file"],"backgroundTag":"missing-required-config-field","analyzedSha":"24702dc5a0214034f4c27166d5fd30cad08cec19","analyzedAt":"2026-09-10T12:36:46.680Z","contentChangedAt":"2026-09-10T12:36:46.680Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}