HMCL-dev/HMCL · error · IllegalArgumentException

profileID is missing

Error message

profileID is missing

What it means

MicrosoftSession.fromStorage rebuilds a session from persisted account metadata and privateData JSON. If the metadata object has no 'profileID' member, IllegalArgumentException('profileID is missing') is thrown — the stored account entry is structurally incomplete and cannot represent a Microsoft session.

Solutions

  1. Delete the affected account entry in HMCL and re-add (re-authenticate) the Microsoft account
  2. Restore the account storage from backup if profileID was present before an edit/migration
  3. If you serialize accounts yourself, always write profileID (UUID string) into metadata alongside privateData tokens
  4. Run completeStorage/normalization or upgrade HMCL so missing fields are repopulated before fromStorage is called

Example fix

// before: blindly deserializing possibly old entries
MicrosoftSession session = MicrosoftSession.fromStorage(metadata, privateData);
// after: guard for legacy entries lacking profileID
if (JsonUtils.getString(metadata, "profileID") == null) {
    throw new UnsupportedStorageFormatException("Account entry predates profileID; re-authenticate this account.");
}
MicrosoftSession session = MicrosoftSession.fromStorage(metadata, privateData);
Defensive patterns

Strategy: validation

Validate before calling

if (metadata == null || JsonUtils.getString(metadata, "profileID") == null) {
    // legacy/corrupt entry — re-authenticate instead of loading
    return Optional.empty();
}

Type guard

boolean hasProfileID(JsonObject metadata) {
    return metadata != null && JsonUtils.getString(metadata, "profileID") != null;
}

Try / catch

try {
    session = MicrosoftSession.fromStorage(metadata, privateData);
} catch (IllegalArgumentException e) {
    if (e.getMessage().equals("profileID is missing")) {
        markAccountForReauth(); // treat as legacy entry
    } else throw e;
}

Prevention

When it happens

Trigger: Loading accounts from storage where the metadata JsonObject lacks profileID: a hand-edited account file, a partially written save (crash mid-write), a storage-format migration that dropped the field, or passing the wrong JsonObject (e.g. privateData instead of metadata).

Common situations: Upgrading HMCL across a storage-format change; manually copying/merging account files between installations; disk corruption or truncation of accounts.json; third-party tooling rewriting account storage without profileID.

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/264f9b918044d4d2. Report an issue: GitHub.

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftSession.java:59

        this.profile = profile;

        if (accessToken != null) Logger.registerAccessToken(accessToken);
    }

    public String getAuthorization() {
        return String.format("%s %s", tokenType(), accessToken());
    }

    /// Returns whether the stored session contains a usable Minecraft profile name.
    public boolean hasProfileName() {
        return profile != null && StringUtils.isNotBlank(profile.name());
    }

    /// Loads a Microsoft session from persisted account metadata and private data.
    public static MicrosoftSession fromStorage(JsonObject metadata, JsonObject privateData) {
        String profileIDText = JsonUtils.getString(metadata, "profileID");
        if (profileIDText == null) {
            throw new IllegalArgumentException("profileID is missing");
        }
        UUID profileID = UUIDs.parse(profileIDText);
        String profileName = JsonUtils.getString(privateData, "profileName", "");
        String tokenType = requireStorageString(privateData, "tokenType");
        String accessToken = requireStorageString(privateData, "accessToken");
        String refreshToken = requireStorageString(privateData, "refreshToken");
        JsonElement notAfterElement = privateData.get("notAfter");
        long notAfter = notAfterElement != null
                && notAfterElement.isJsonPrimitive()
                && notAfterElement.getAsJsonPrimitive().isNumber()
                ? notAfterElement.getAsLong()
                : 0L;
        String userId = requireStorageString(privateData, "userid");
        return new MicrosoftSession(tokenType, accessToken, notAfter, refreshToken, new User(userId), new GameProfile(profileID, profileName));
    }

    /// Writes this session to persisted private account data.
    public void writePrivateData(JsonObject privateData) {

View on GitHub (pinned to 24702dc5a0)