HMCL-dev/HMCL · error · IllegalArgumentException
is missing
Error message
is missing
What it means
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.
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
Example fix
// before: parsing privateData with required keys assumed
JsonObject privateData = entry.getAsJsonObject("privateData");
MicrosoftSession session = MicrosoftSession.fromStorage(metadata, privateData);
// after: pre-check required keys and treat entry as legacy if absent
java.util.Set<String> required = java.util.Set.of("tokenType", "accessToken", "refreshToken", "userId");
for (String key : required) {
if (JsonUtils.getString(privateData, key) == null) {
throw new UnsupportedStorageFormatException("Account entry missing '" + key + "'; re-authenticate.");
}
}
MicrosoftSession session = MicrosoftSession.fromStorage(metadata, privateData); Defensive patterns
Strategy: validation
Validate before calling
java.util.Set<String> required = java.util.Set.of("tokenType","accessToken","refreshToken","userId");
for (String key : required) {
if (JsonUtils.getString(privateData, key) == null) {
return Optional.empty(); // incomplete entry, needs re-auth
}
} Type guard
boolean hasAllSessionKeys(JsonObject privateData) {
return java.util.stream.Stream.of("tokenType","accessToken","refreshToken","userId")
.allMatch(k -> JsonUtils.getString(privateData, k) != null);
} Try / catch
try {
session = MicrosoftSession.fromStorage(metadata, privateData);
} catch (IllegalArgumentException e) {
if (e.getMessage() != null && e.getMessage().endsWith(" is missing")) {
markAccountForReauth(e.getMessage()); // e.g. 'refreshToken is missing'
} else throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
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
- profileID is missing
- Missing protected payload member: nonce
- 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/3d17e0c33d5798aa.
Report an issue: GitHub.
Appendix: source
Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftSession.java:93
/// Writes this session to persisted private account data.
public void writePrivateData(JsonObject privateData) {
requireNonNull(profile);
requireNonNull(user);
privateData.addProperty("profileName", profile.name());
privateData.addProperty("tokenType", tokenType);
privateData.addProperty("accessToken", accessToken);
privateData.addProperty("refreshToken", refreshToken);
privateData.addProperty("notAfter", notAfter);
privateData.addProperty("userid", user.id);
}
/// Reads a required string member from account storage.
private static String requireStorageString(JsonObject storage, String name) {
String value = JsonUtils.getString(storage, name);
if (value == null) {
throw new IllegalArgumentException(name + " is missing");
}
return value;
}
public AuthInfo toAuthInfo() {
requireNonNull(profile);
return new AuthInfo(profile.name(), profile.id(), accessToken, AuthInfo.USER_TYPE_MSA, "{}");
}
@JsonSerializable
public record User(String id) {
}
@JsonSerializable
public record GameProfile(UUID id, String name) {
}
}View on GitHub (pinned to 24702dc5a0)