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

  1. Pass an empty array (new String[0]) instead of null when no features are required
  2. Validate the feature list source (config/Bundle) before calling
  3. 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

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)