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

  1. Identify the missing field from the message (e.g. 'refreshToken is missing') and delete/re-add the account in HMCL to regenerate all tokens
  2. Restore a backup of the account storage taken before the corruption/edit
  3. If constructing storage programmatically, write every required key: tokenType, accessToken, refreshToken, userId, and profileName
  4. 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

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


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)