HMCL-dev/HMCL · error · IllegalArgumentException

storage does not have loginName

Error message

storage does not have loginName

What it means

The full fromStorage overload reads the "loginName" key from metadata after reconstructing the account ID and session; a null value throws IllegalArgumentException. The login name is required to rehydrate the account, so its absence means the persisted record is incomplete.

Solutions

  1. Persist "loginName" in the account metadata when saving
  2. Re-login the account to regenerate complete storage
  3. During migration, map the legacy username field to loginName

Example fix

// before
JsonObject meta = new JsonObject(); // no loginName
factory.fromStorage(meta, privateData, downloader, server);
// after
meta.addProperty("loginName", storedUsername);
factory.fromStorage(meta, privateData, downloader, server);
Defensive patterns

Strategy: validation

Validate before calling

if (!metadata.has("loginName")) {
    throw new IllegalArgumentException("Persisted account missing loginName; re-login required");
}
AuthlibInjectorAccount account = factory.fromStorage(metadata, privateData, downloader, server);

Try / catch

try {
    return factory.fromStorage(metadata, privateData, downloader, server);
} catch (IllegalArgumentException e) {
    log.warn("Account storage incomplete: {}", e.getMessage());
    return null;
}

Prevention

When it happens

Trigger: Calling the AuthlibInjectorAccountFactory.fromStorage overload with metadata missing the "loginName" property.

Common situations: Storage entries written by older HMCL versions before loginName was persisted; manually trimmed accounts.json; schema migration bugs.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10). Data as JSON: /api/errors/68fd30b69f895bc9. Report an issue: GitHub.

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorAccountFactory.java:88

        String apiRoot = JsonUtils.getString(metadata, "serverBaseURL");
        if (apiRoot == null) {
            throw new IllegalArgumentException("storage does not have API root.");
        }
        AuthlibInjectorServer server = serverLookup.apply(apiRoot);
        return fromStorage(metadata, privateData, downloader, server);
    }

    static AuthlibInjectorAccount fromStorage(
            JsonObject metadata,
            JsonObject privateData,
            AuthlibInjectorArtifactProvider downloader,
            AuthlibInjectorServer server) {
        AccountID accountID = Account.readAccountID(metadata);
        YggdrasilSession session = YggdrasilSession.fromStorage(metadata, privateData);

        String loginName = JsonUtils.getString(metadata, "loginName");
        if (loginName == null) {
            throw new IllegalArgumentException("storage does not have loginName");
        }

        if (privateData.get("profileProperties") instanceof JsonObject profilePropertiesObject) {
            Map<String, String> properties = JsonUtils.GSON.fromJson(
                    profilePropertiesObject,
                    JsonUtils.mapTypeOf(String.class, String.class));
            GameProfile selected = session.getSelectedProfile();
            ObservableOptionalCache<UUID, CompleteGameProfile, AuthenticationException> profileRepository =
                    server.getYggdrasilService().getProfileRepository();
            profileRepository.put(selected.getId(), new CompleteGameProfile(selected, properties));
            profileRepository.invalidate(selected.getId());
        }

        return new AuthlibInjectorAccount(accountID, server, downloader, loginName, session);
    }
}

View on GitHub (pinned to 24702dc5a0)