asLody/VirtualApp · error · java.lang.IllegalArgumentException
features is null
Error message
features is null
What it means
hasFeatures(userId, response, account, features) throws IllegalArgumentException('features is null') as its third guard. The features array is required to determine whether the account's authenticator supports all requested capabilities; the downstream feature-check session cannot run without it.
Solutions
- Pass an empty array (new String[0]) instead of null when no features are required
- Validate the feature list source (config/Bundle) before calling
- Guard: check response, account, and features all non-null before invoking hasFeatures
Example fix
// before service.hasFeatures(userId, response, account, features); // features == null // after if (features == null) features = new String[0]; service.hasFeatures(userId, response, account, features);
Defensive patterns
Strategy: validation
Validate before calling
if (features == null) { features = new String[0]; } Type guard
String[] safeFeatures(String[] f) { return f == null ? new String[0] : f; } Prevention
- Use empty arrays, never null, to mean 'no features'
- Validate feature lists loaded from config/server before calling
- Wrap feature probes in a helper that normalizes null inputs
When it happens
Trigger: Calling hasFeatures with a null features array — typically a String[] built from a Bundle or preferences that was never populated, or an API wrapper forwarding an uninitialized array.
Common situations: Probing for features like 'service_xmpp' where the feature list comes from server config that failed to load; passing null intending 'no features' instead of an empty array.
Related errors
AI-assisted analysis of asLody/VirtualApp@666fefcb5d (2026-09-09).
Data as JSON: /api/errors/31ce3fcf226caef5.
Report an issue: GitHub.
Appendix: source
Thrown at VirtualApp/lib/src/main/java/com/lody/virtual/server/accounts/VAccountManagerService.java:378
public void setUserData(int userId, Account account, String key, String value) {
if (key == null) throw new IllegalArgumentException("key is null");
if (account == null) throw new IllegalArgumentException("account is null");
VAccount vAccount = getAccount(userId, account);
if (vAccount != null) {
synchronized (accountsByUserId) {
vAccount.userDatas.put(key, value);
saveAllAccounts();
}
}
}
@Override
public void hasFeatures(int userId, IAccountManagerResponse response,
final Account account, final String[] features) {
if (response == null) throw new IllegalArgumentException("response is null");
if (account == null) throw new IllegalArgumentException("account is null");
if (features == null) throw new IllegalArgumentException("features is null");
AuthenticatorInfo info = this.getAuthenticatorInfo(account.type);
if (info == null) {
try {
response.onError(ERROR_CODE_BAD_ARGUMENTS, "account.type does not exist");
} catch (RemoteException e) {
e.printStackTrace();
}
return;
}
new Session(response, userId, info, false, true, account.name) {
@Override
public void run() throws RemoteException {
try {
mAuthenticator.hasFeatures(this, account, features);
} catch (RemoteException e) {
onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
}View on GitHub (pinned to 666fefcb5d)