{"record":{"id":"edb8652d560f17f5","repo":"HMCL-dev/HMCL","slug":"protected-payload-is-not-a","errorCode":null,"errorMessage":"Protected payload is not a ","messagePattern":"Protected payload is not a ","errorType":"validation","errorClass":"JsonParseException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/setting/ProtectedPayload.java","lineNumber":311,"sourceCode":"        /// Reads the payload from the given envelope.\n        ///\n        /// @param envelope the envelope object to read from\n        /// @return the revealed JSON payload\n        /// @throws JsonParseException if the envelope is malformed or cannot be revealed\n        protected abstract JsonElement readPayload(JsonObject envelope);\n\n        /// Reads the payload from the given envelope and checks its JSON element type.\n        ///\n        /// @param envelope the envelope object to read from\n        /// @param payloadType the expected JSON element type\n        /// @return the revealed JSON payload\n        /// @param <T> the expected JSON element type\n        /// @throws JsonParseException if the envelope is malformed, cannot be revealed, or has the wrong payload type\n        public final <T extends JsonElement> T read(JsonObject envelope, Class<T> payloadType) {\n            Objects.requireNonNull(payloadType);\n            JsonElement payload = readPayload(envelope);\n            if (!payloadType.isInstance(payload)) {\n                throw new JsonParseException(\"Protected payload is not a \" + payloadType.getSimpleName());\n            }\n            return payloadType.cast(payload);\n        }\n\n        /// Returns the write mode selected by a configuration value.\n        ///\n        /// Unknown values intentionally fall back to obfuscation so opt-in plain storage cannot be enabled by typos.\n        ///\n        /// @param id the configured protection marker\n        /// @return the selected write mode\n        static ProtectionMode fromConfiguredId(@Nullable String id) {\n            for (ProtectionMode mode : values()) {\n                if (mode.id.equals(id)) {\n                    return mode;\n                }\n            }\n            return OBFUSCATED_V1;\n        }","sourceCodeStart":293,"sourceCodeEnd":329,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/setting/ProtectedPayload.java#L293-L329","documentation":"Thrown by ProtectionMode.read(envelope, payloadType) after the payload is successfully revealed but is not an instance of the requested Gson element type. The envelope decrypted fine; the caller simply asked for the wrong JSON element type (e.g. JsonObject when the payload is actually a JsonPrimitive or JsonArray). Note the message uses payloadType.getSimpleName(), so it names the EXPECTED type, not the actual one.","triggerScenarios":"ProtectedPayload.read(envelope, JsonObject.class) on an envelope whose payload is a JsonArray, JsonPrimitive, or JsonNull; likewise read(..., JsonArray.class) on an object payload; calling with a concrete type when the writer serialized a different element shape.","commonSituations":"A schema change moved the payload from an object to an array (or wrapped it) between versions; requesting JsonObject by habit when the stored value is a string/number; reading an envelope written by a different feature that stores scalars.","solutions":["Inspect the actual payload shape (e.g. reveal once with JsonElement.class) and request the matching Class: JsonObject, JsonArray, JsonPrimitive, or JsonNull.","Update the reader to the current schema if the payload layout changed in a newer version of the writing code.","If the shape is variable, call read(envelope, JsonElement.class) and switch on isJsonObject()/isJsonArray()/isJsonPrimitive() before consuming."],"exampleFix":"// before\nJsonObject obj = ProtectedPayload.read(envelope, JsonObject.class); // payload is actually an array\n// after\nJsonElement element = ProtectedPayload.read(envelope, JsonElement.class);\nif (!element.isJsonObject()) {\n    throw new JsonParseException(\"Expected object payload, got: \" + element);\n}\nJsonObject obj = element.getAsJsonObject();","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"static JsonObject readObjectPayload(JsonObject envelope) {\n    JsonElement payload = ProtectedPayload.read(envelope, JsonElement.class);\n    if (!payload.isJsonObject()) {\n        throw new JsonParseException(\"Expected JsonObject payload, got: \" + payload.getClass().getSimpleName());\n    }\n    return payload.getAsJsonObject();\n}","tryCatchPattern":"try {\n    return ProtectedPayload.read(envelope, JsonObject.class);\n} catch (JsonParseException e) {\n    throw new SchemaMismatchException(\"Payload does not match expected shape\", e);\n}","preventionTips":["Read as JsonElement and narrow with isJsonObject()/isJsonArray() before requesting a concrete type.","Keep writer and reader payloads in the same schema module/tests to catch shape drift early.","Remember the message names the EXPECTED type — check what the writer actually serialized."],"tags":["json","type-mismatch","gson","hmcl"],"backgroundTag":"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"}