HMCL-dev/HMCL · error · JsonParseException
Missing payload
Error message
Missing payload
What it means
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().
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.
Example fix
// before: hand-crafted envelope
{"protection": "plain"}
// after: envelope with payload present
{"protection": "plain", "payload": {"accounts": []}} Defensive patterns
Strategy: validation
Validate before calling
if (envelope.has("payload")) {
ProtectedPayload.read(envelope, JsonElement.class);
} else {
// fall back to defaults or recreate the setting
} Type guard
static boolean hasPlainPayload(com.google.gson.JsonObject envelope) {
return envelope.has("payload") && !envelope.get("payload").isJsonNull();
} Try / catch
try {
JsonElement payload = ProtectedPayload.read(envelope, JsonObject.class);
} catch (com.google.gson.JsonParseException e) {
logger.warning("Envelope missing payload, using defaults", e);
return defaults();
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- Account private data is not an object
- Config is not an object:
- e
- Game directory ID cannot be nil
- Game directory ID cannot be null
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/1531944a39841166.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/setting/ProtectedPayload.java:76
/// Selects how a protected payload is stored in its JSON envelope.
@NotNullByDefault
enum ProtectionMode {
/// Stores the payload as plain JSON for development and diagnostics.
PLAIN("plain") {
/// Writes the payload into the given envelope.
@Override
protected void writePayload(JsonObject envelope, JsonElement payload) {
envelope.addProperty(PROPERTY_PROTECTION, id());
envelope.add(PROPERTY_PAYLOAD, payload);
}
/// Reads the payload from the given envelope.
@Override
protected JsonElement readPayload(JsonObject envelope) {
JsonElement payload = envelope.get(PROPERTY_PAYLOAD);
if (payload == null) {
throw new JsonParseException("Missing payload");
}
return payload;
}
},
/// Stores the payload as a portable weakly encrypted envelope.
///
/// The payload is encrypted with a built-in application key, Base64-encoded, and split into padded strings. This
/// avoids storing private data as directly readable JSON, but it should not be treated as device-bound secret
/// storage. The nonce is stored separately, while the payload lanes store `ciphertext || authentication tag`.
OBFUSCATED_V1("hmcl-obfuscated-v1") {
/// The number of lanes used by the obfuscated payload.
private static final int OBFUSCATED_LANE_COUNT = 4;
/// The number of padding elements stored before each lane.
private static final int LANE_PADDING_COUNT = 63;
/// The number of elements written in an obfuscated payload array.View on GitHub (pinned to 24702dc5a0)