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
- Persist "loginName" in the account metadata when saving
- Re-login the account to regenerate complete storage
- 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
- Serialize loginName in every account save
- Check for all required keys (accountID, loginName, serverBaseURL) before deserializing
- Drop/re-login legacy storage entries lacking loginName
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
- storage does not have API root.
- accountID is missing
- Malformed response\n" + text
- authlib-injectors.json -> urls cannot be null.
- "Invalid metadata file: " + jarInJarMetadata
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)