{"record":{"id":"3d17e0c33d5798aa","repo":"HMCL-dev/HMCL","slug":"is-missing","errorCode":null,"errorMessage":" is missing","messagePattern":" is missing","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftSession.java","lineNumber":93,"sourceCode":"\n    /// Writes this session to persisted private account data.\n    public void writePrivateData(JsonObject privateData) {\n        requireNonNull(profile);\n        requireNonNull(user);\n\n        privateData.addProperty(\"profileName\", profile.name());\n        privateData.addProperty(\"tokenType\", tokenType);\n        privateData.addProperty(\"accessToken\", accessToken);\n        privateData.addProperty(\"refreshToken\", refreshToken);\n        privateData.addProperty(\"notAfter\", notAfter);\n        privateData.addProperty(\"userid\", user.id);\n    }\n\n    /// Reads a required string member from account storage.\n    private static String requireStorageString(JsonObject storage, String name) {\n        String value = JsonUtils.getString(storage, name);\n        if (value == null) {\n            throw new IllegalArgumentException(name + \" is missing\");\n        }\n        return value;\n    }\n\n    public AuthInfo toAuthInfo() {\n        requireNonNull(profile);\n\n        return new AuthInfo(profile.name(), profile.id(), accessToken, AuthInfo.USER_TYPE_MSA, \"{}\");\n    }\n\n    @JsonSerializable\n    public record User(String id) {\n    }\n\n    @JsonSerializable\n    public record GameProfile(UUID id, String name) {\n    }\n}","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftSession.java#L75-L111","documentation":"requireStorageString reads a mandatory string member from the session's persisted JSON. If the named key (tokenType, accessToken, refreshToken, userId, ...) is absent or null, IllegalArgumentException('<name> is missing') is thrown with the key name in the message, indicating the stored privateData is incomplete for reconstructing the session.","triggerScenarios":"MicrosoftSession.fromStorage calling requireStorageString for tokenType/accessToken/refreshToken/userId when that key is missing from privateData: truncated or hand-edited account storage, migration between HMCL storage formats dropping fields, or crash during save.","commonSituations":"Partial account file after an unexpected exit; manually editing accounts.json and omitting a token field; old-format entries migrated to a schema expecting new required fields; merging account data from multiple installs.","solutions":["Identify the missing field from the message (e.g. 'refreshToken is missing') and delete/re-add the account in HMCL to regenerate all tokens","Restore a backup of the account storage taken before the corruption/edit","If constructing storage programmatically, write every required key: tokenType, accessToken, refreshToken, userId, and profileName","Upgrade HMCL so its storage migration populates new required fields for legacy entries"],"exampleFix":"// before: parsing privateData with required keys assumed\nJsonObject privateData = entry.getAsJsonObject(\"privateData\");\nMicrosoftSession session = MicrosoftSession.fromStorage(metadata, privateData);\n// after: pre-check required keys and treat entry as legacy if absent\njava.util.Set<String> required = java.util.Set.of(\"tokenType\", \"accessToken\", \"refreshToken\", \"userId\");\nfor (String key : required) {\n    if (JsonUtils.getString(privateData, key) == null) {\n        throw new UnsupportedStorageFormatException(\"Account entry missing '\" + key + \"'; re-authenticate.\");\n    }\n}\nMicrosoftSession session = MicrosoftSession.fromStorage(metadata, privateData);","handlingStrategy":"validation","validationCode":"java.util.Set<String> required = java.util.Set.of(\"tokenType\",\"accessToken\",\"refreshToken\",\"userId\");\nfor (String key : required) {\n    if (JsonUtils.getString(privateData, key) == null) {\n        return Optional.empty(); // incomplete entry, needs re-auth\n    }\n}","typeGuard":"boolean hasAllSessionKeys(JsonObject privateData) {\n    return java.util.stream.Stream.of(\"tokenType\",\"accessToken\",\"refreshToken\",\"userId\")\n            .allMatch(k -> JsonUtils.getString(privateData, k) != null);\n}","tryCatchPattern":"try {\n    session = MicrosoftSession.fromStorage(metadata, privateData);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().endsWith(\" is missing\")) {\n        markAccountForReauth(e.getMessage()); // e.g. 'refreshToken is missing'\n    } else throw e;\n}","preventionTips":["Allow save operations to complete; don't terminate the launcher mid-write","Back up account storage before manual edits or migrations","Serialize all required keys (tokenType, accessToken, refreshToken, userId) when writing storage yourself","Surface the key name in the exception to the user so they know which token to regenerate"],"tags":["storage","serialization","missing-field","account"],"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-15T23:17:13.987Z"}