HMCL-dev/HMCL · error · JsonParseException
Missing payload or payload array is too small
Error message
Missing payload or payload array is too small
What it means
OBFUSCATED_V1.joinObfuscatedPayload throws this when the envelope's 'payload' member is not a JSON array or has fewer than 4 elements. The obfuscated format stores the Base64 ciphertext in a 256-element null-padded array; anything smaller cannot contain the 4 lanes and is considered malformed.
Solutions
- Ensure the protection marker matches the actual payload layout; use 'plain' with a direct payload, or a full obfuscated array.
- Restore the original 256-element padded array (or the whole file) from backup and let HMCL re-save.
- Regenerate the setting so HMCL rewrites the envelope in the correct format.
Example fix
// before: mismatched marker/layout
{"protection":"hmcl-obfuscated-v1","payload":"abc...","nonce":"..."}
// after: correct pairing
{"protection":"plain","payload":{...}} Defensive patterns
Strategy: validation
Validate before calling
com.google.gson.JsonElement p = envelope.get("payload");
boolean ok = "hmcl-obfuscated-v1".equals(JsonUtils.getString(envelope, "protection"))
? p instanceof com.google.gson.JsonArray && ((com.google.gson.JsonArray) p).size() >= 4
: p != null; Type guard
static boolean hasObfuscatedPayloadArray(com.google.gson.JsonObject envelope) {
return envelope.get("payload") instanceof com.google.gson.JsonArray lanes
&& lanes.size() >= 4;
} Try / catch
try {
return ProtectedPayload.read(envelope, JsonObject.class);
} catch (com.google.gson.JsonParseException e) {
logger.warning("Malformed obfuscated envelope; regenerating", e);
return recreateSetting();
} Prevention
- Match the protection marker to the actual payload layout (plain vs obfuscated).
- Never shrink or reorder the 256-element payload array.
- Validate envelopes programmatically after external tools modify them.
- Regenerate settings rather than hand-crafting obfuscated envelopes.
When it happens
Trigger: Calling ProtectedPayload.read on an envelope marked 'hmcl-obfuscated-v1' whose payload member is missing, not an array (e.g. a plain value from PLAIN mode), or an array with fewer than OBFUSCATED_LANE_COUNT (4) entries.
Common situations: Mixing envelope formats — using a 'plain'-style payload with an 'hmcl-obfuscated-v1' marker; hand-editing and shortening the padded array; older or third-party writers emitting a different obfuscation layout.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- json.toString()
- json.toString()
- e
- Account private data is not an object
- Game directory ID cannot be null
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/bc87d8ed3c0e947f.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/setting/ProtectedPayload.java:186
int start = laneIndex * laneLength;
String lane = payload.substring(start, start + laneLength);
for (int i = 0; i < LANE_PADDING_COUNT; i++) {
result.add(JsonNull.INSTANCE);
}
result.add(lane);
}
return result;
}
/// Joins Base64 payload lanes from the envelope.
///
/// @param envelope the envelope object to read from
/// @return the restored Base64 payload
/// @throws JsonParseException if the payload lanes are missing or malformed
private static String joinObfuscatedPayload(JsonObject envelope) {
if (!(envelope.get(PROPERTY_PAYLOAD) instanceof JsonArray lanes)
|| lanes.size() < OBFUSCATED_LANE_COUNT) {
throw new JsonParseException("Missing payload or payload array is too small");
}
int effectivePayloadSize = Integer.highestOneBit(lanes.size());
String[] laneTexts = new String[OBFUSCATED_LANE_COUNT];
int totalLength = 0;
for (int i = 0; i < OBFUSCATED_LANE_COUNT; i++) {
int payloadIndex = lanePayloadIndex(i, effectivePayloadSize);
JsonElement lane = lanes.get(payloadIndex);
if (!lane.isJsonPrimitive() || !lane.getAsJsonPrimitive().isString()) {
throw new JsonParseException("Protected payload lane is not a string");
}
laneTexts[i] = lane.getAsString();
totalLength += laneTexts[i].length();
}
StringBuilder result = new StringBuilder(totalLength);
for (String laneText : laneTexts) {View on GitHub (pinned to 24702dc5a0)