HMCL-dev/HMCL · error · JsonParseException
Missing protected payload member: nonce
Error message
Missing protected payload member: nonce
What it means
Thrown by OBFUSCATED_V1.readPayload when the JSON envelope declares protection mode hmcl-obfuscated-v1 but has no "nonce" member (JsonUtils.getString returns null). Without the nonce stored alongside the ciphertext, the ChaCha20-Poly1305 payload cannot be decrypted, so the library fails fast with a descriptive JsonParseException.
Solutions
- Regenerate the envelope through ProtectionMode.writePayload so the nonce, payload lanes, and protection marker are all written consistently.
- If the data is recoverable, restore the missing "nonce" member (Base64, 16 chars) from a backup of the original file.
- Check the writer path: ensure no serializer/migration step strips the nonce member between write and read.
Example fix
// before (hand-built envelope)
JsonObject envelope = new JsonObject();
envelope.addProperty("protection", "hmcl-obfuscated-v1");
envelope.add("payload", lanes);
// after
mode.writePayload(envelope, payload); // writes protection, payload lanes, and nonce Defensive patterns
Strategy: validation
Validate before calling
static boolean hasNonce(JsonObject envelope) {
return envelope.has("nonce") && envelope.get("nonce").isJsonPrimitive()
&& envelope.get("nonce").getAsString() != null;
} Type guard
static boolean isObfuscatedEnvelopeWithNonce(JsonObject envelope) {
return envelope.has("protection") && "hmcl-obfuscated-v1".equals(envelope.get("protection").getAsString())
&& envelope.has("nonce");
} Try / catch
try {
return ProtectedPayload.read(envelope, JsonObject.class);
} catch (JsonParseException e) {
LOG.warning("Envelope missing/corrupt members; rebuilding", e);
return rebuildEnvelope();
} Prevention
- Always build envelopes via ProtectionMode.writePayload instead of assembling JSON by hand.
- Configure JSON migrations/serializers to preserve unknown members like "nonce".
- Validate envelope completeness (protection, nonce, payload array) at load time before use.
When it happens
Trigger: Calling ProtectedPayload.read(envelope, type) (or ProtectionMode.fromEnvelope(...).readPayload) on an envelope where envelope.get("nonce") is absent or JSON null — e.g. a hand-written envelope, one produced by an older/other writer, or one where the nonce member was renamed or stripped by a sanitizer.
Common situations: Manually editing or partial-copying a settings JSON; a schema migration dropping unknown members; another tool round-tripping the JSON and omitting fields it does not understand; constructing the envelope in code and forgetting envelope.addProperty("nonce", ...).
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
- Protected payload nonce has invalid length
- Protected payload is not a
- Theme background field is missing:
- Theme color source is missing required field:
- Missing author name:
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/512e37f699ca7fff.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/setting/ProtectedPayload.java:241
}
String payloadText = JsonUtils.UGLY_GSON.toJson(payload);
byte[] payloadBytes = payloadText.getBytes(StandardCharsets.UTF_8);
byte[] encryptedPayload = encryptPayload(payloadBytes, nonce);
String actualPayload = Base64.getEncoder().encodeToString(encryptedPayload);
envelope.addProperty(PROPERTY_PROTECTION, id());
envelope.add(PROPERTY_PAYLOAD, splitObfuscatedPayload(actualPayload));
envelope.addProperty(PROPERTY_NONCE, Base64.getEncoder().encodeToString(nonce));
}
/// Reads the payload from the given envelope.
@Override
protected JsonElement readPayload(JsonObject envelope) {
try {
String encodedNonce = JsonUtils.getString(envelope, PROPERTY_NONCE);
if (encodedNonce == null) {
throw new JsonParseException("Missing protected payload member: nonce");
}
byte[] nonce = Base64.getDecoder().decode(encodedNonce);
if (nonce.length != NONCE_SIZE) {
throw new JsonParseException("Protected payload nonce has invalid length");
}
String encodedPayload = joinObfuscatedPayload(envelope);
byte[] encryptedPayload = Base64.getDecoder().decode(encodedPayload);
byte[] payloadBytes = decryptPayload(encryptedPayload, nonce);
return JsonParser.parseString(new String(payloadBytes, StandardCharsets.UTF_8));
} catch (IllegalArgumentException e) {
throw new JsonParseException("Failed to reveal protected JSON payload", e);
}
}
};
/// The serialized protection marker.View on GitHub (pinned to 24702dc5a0)