xpipe-io/xpipe · error · IllegalArgumentException

Unable to change scope from raw value with null value

Error message

Unable to change scope from raw value with null value

What it means

OptionalEncryptedValue.with(value, secret, scope) refuses to apply a scope change to a value that has no encrypted content (value == null) but is currently a raw value, because with null content there is no secret object to re-encrypt and a raw value cannot itself carry a scope restriction. The library throws IllegalArgumentException since the argument combination (null value + raw value + new scope) is invalid.

Source

Thrown at app/src/main/java/io/xpipe/app/secret/OptionalEncryptedValue.java:89

    public OptionalEncryptedValue<T> withUpdatedPrincipals() {
        return with(
                value,
                isEncrypted() ? DataStoreAccessScope.getTargetScope(getSecret().getScope()) : null);
    }

    public OptionalEncryptedValue<T> with(T value, DataStoreAccessScope scope) {
        if (value == null && scope == null) {
            return null;
        }

        var encryptionUnchanged = (secret == null && scope == null)
                || (secret != null && scope != null && secret.getScope().equals(scope) && secret.isScopeValid());

        // If we don't have a value, we can only restrict the scope further
        if (value == null) {
            if (isRaw()) {
                throw new IllegalArgumentException("Unable to change scope from raw value with null value");
            }

            if (!encryptionUnchanged) {
                var newSecret = secret.with(null, scope);
                return new OptionalEncryptedValue<>(null, null, newSecret);
            }

            return this;
        }

        var newValueJson = JacksonMapper.getDefault().valueToTree(value);

        if (value.equals(this.value) && newValueJson.equals(this.valueJson) && encryptionUnchanged) {
            return this;
        }

        if (newValueJson.equals(this.valueJson) && encryptionUnchanged) {
            return new OptionalEncryptedValue<>(newValueJson, value, secret);

View on GitHub (pinned to d85ca821ba)

Solutions

  1. Only change scope on values that actually hold secret content: skip entries where value == null && isRaw().
  2. Provide a non-null value along with the scope change so the value can be encrypted for the new scope.
  3. If restricting scope of an empty value is intended, ensure the underlying secret is non-null so secret.with(null, scope) can run.

Example fix

// before
value.with(null, secret, newScope); // throws for raw null values
// after
if (value.getValue() != null || !value.isRaw()) {
    value.with(null, secret, newScope);
} // else skip: nothing to re-scope
Defensive patterns

Strategy: type-guard

Validate before calling

boolean canChangeScope = value.getValue() != null || !value.isRaw();

Type guard

boolean rescopeAllowed(OptionalEncryptedValue<?> v) { return v.getValue() != null || !v.isRaw(); }

Try / catch

try {
    value.with(null, secret, scope);
} catch (IllegalArgumentException e) {
    // skip raw null values; nothing to re-scope
}

Prevention

When it happens

Trigger: Calling with(null, ...) on an OptionalEncryptedValue whose value is null and isRaw() is true, while passing a non-null scope (or a secret that changes the scope); typically reached via withUpdatedPrincipals() when the stored entry has no encrypted value yet.

Common situations: Programmatically updating access scope/principals on a freshly created store entry that has never been saved with an actual secret value; bulk scope-migration scripts touching placeholder/raw entries.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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