xpipe-io/xpipe · error · IllegalArgumentException

Principals must not be empty

Error message

Principals must not be empty

What it means

DataStoreAccessScope is an immutable scope of encryption principals; its private constructor requires at least one principal because an access scope with no principals is meaningless — nothing could decrypt data in it. It throws IllegalArgumentException immediately when handed an empty set.

Source

Thrown at app/src/main/java/io/xpipe/app/storage/DataStoreAccessScope.java:101

    public static DataStoreAccessScope vault() {
        return new DataStoreAccessScope(
                Set.of(DataStorageAccessHandler.getInstance().getFallbackPrincipal()));
    }

    public static DataStoreAccessScope encryption() {
        return new DataStoreAccessScope(
                Set.of(DataStorageAccessHandler.getInstance().getEncryptAllPrincipal()));
    }

    public static DataStoreAccessScope of(Set<EncryptionPrincipal> encryptionPrincipals) {
        return new DataStoreAccessScope(encryptionPrincipals);
    }

    private final Set<EncryptionPrincipal> principals;

    private DataStoreAccessScope(Set<EncryptionPrincipal> principals) {
        if (principals.isEmpty()) {
            throw new IllegalArgumentException("Principals must not be empty");
        }

        var vault = DataStorageAccessHandler.getInstance().getFallbackPrincipal();
        var encrypt = DataStorageAccessHandler.getInstance().getEncryptAllPrincipal();
        if (principals.contains(vault)) {
            this.principals = treeSet(Set.of(vault));
        } else if (principals.contains(encrypt)) {
            this.principals = treeSet(Set.of(encrypt));
        } else {
            this.principals = treeSet(principals);
        }
    }

    public boolean isAccessSubRestricted() {
        var all = this.equals(encryption()) || this.equals(vault());
        return !all;
    }

View on GitHub (pinned to d85ca821ba)

Solutions

  1. Ensure the principal set has at least one entry before constructing the scope; bail out earlier with a clear message if the list is empty.
  2. Fall back to the vault principal (DataStorageAccessHandler.getInstance().getFallbackPrincipal()) when no user principals are available.
  3. Fix the upstream filtering logic so allowed principals are actually collected.

Example fix

// before
var scope = DataStoreAccessScope.of(collectedPrincipals); // throws when empty
// after
if (collectedPrincipals.isEmpty()) {
    collectedPrincipals = Set.of(DataStorageAccessHandler.getInstance().getFallbackPrincipal());
}
var scope = DataStoreAccessScope.of(collectedPrincipals);
Defensive patterns

Strategy: validation

Validate before calling

if (principals == null || principals.isEmpty()) {
    throw new IllegalArgumentException("At least one encryption principal is required");
}

Try / catch

try {
    var scope = DataStoreAccessScope.of(principals);
} catch (IllegalArgumentException e) {
    // fall back to the vault principal
}

Prevention

When it happens

Trigger: Calling DataStoreAccessScope.of()/factory or the private constructor path with an empty Set<EncryptionPrincipal>, e.g. after filtering principals and getting an empty result, or building a scope from a collection that was never populated.

Common situations: Computing the intersection of allowed principals across users and getting an empty set; loading principal lists from config/vault where none were found; filtering out principals the current user cannot access.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of xpipe-io/xpipe@d85ca821ba (2026-09-06). Data as JSON: /api/errors/62ee2d9a0ff84c0d. Report an issue: GitHub.