{"record":{"id":"a171ec79dda5484d","repo":"xpipe-io/xpipe","slug":"no-secret-available-to-encrypt","errorCode":null,"errorMessage":"No secret available to encrypt","messagePattern":"No secret available to encrypt","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/io/xpipe/app/secret/MultiPrincipalSecret.java","lineNumber":209,"sourceCode":"        for (EncryptionPrincipal principal : scope.getPrincipals()) {\n            var existingEntry = entries.stream()\n                    .filter(entry -> entry.getPrincipal().equals(principal))\n                    .findFirst();\n\n            // Keep existing entry if possible if not accessible\n            if (!principal.isAccessible()) {\n                if (existingEntry.isPresent()) {\n                    l.add(existingEntry.get());\n                }\n                continue;\n            }\n\n            if (existingEntry.isPresent()) {\n                var principalUnchanged = existingEntry.get().getToken().matches(principal);\n                var keep = secretUnchanged && principalUnchanged;\n\n                if (!keep && secret == null) {\n                    throw new IllegalArgumentException(\"No secret available to encrypt\");\n                }\n\n                l.add(new Entry(\n                        principal,\n                        keep\n                                ? existingEntry.get().getEncrypted()\n                                : AesSecretValue.encrypt(secret.getSecret(), principal.getSecretKey())\n                                        .getEncryptedValue(),\n                        keep ? iteration : iteration + 1,\n                        keep ? existingEntry.get().getToken() : EncryptionToken.of(principal)));\n            } else {\n                if (secret == null) {\n                    throw new IllegalArgumentException(\"No secret available to encrypt\");\n                }\n\n                var enc = AesSecretValue.encrypt(secret.getSecret(), principal.getSecretKey());\n                l.add(new Entry(principal, enc.getEncryptedValue(), iteration + 1, EncryptionToken.of(principal)));\n            }","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/xpipe-io/xpipe/blob/d85ca821baa46092a320ebb13546d7240adb74f8/app/src/main/java/io/xpipe/app/secret/MultiPrincipalSecret.java#L191-L227","documentation":"In with(), when a principal already has an existing entry but the secret or principal changed (keep == false), a new ciphertext must be produced. If the caller passed a null secret in that situation, there is nothing to encrypt and an IllegalArgumentException is thrown. Passing the same secret and same principal (keep == true) avoids the throw.","triggerScenarios":"Calling with(null, scope) / withUpdatedPrincipals with a null secret while the new principal differs from the existing entry's token, so keep == false and encryption is required.","commonSituations":"Clearing a secret by passing null but also changing the principal set; building update flows that pass null for 'unchanged' while principals were rotated.","solutions":["Pass the actual secret value when principals change","If you intend to clear the secret, also remove the corresponding principal from the set","Reuse the existing MultiPrincipalSecret instance when secret is unchanged (it returns this)"],"exampleFix":"// before\nsecret.with(null, scope); // principals changed -> throw\n// after\nsecret.with(currentSecretValue, scope);","handlingStrategy":"validation","validationCode":"if (secret == null && principalsChanged) {\n    throw new IllegalArgumentException(\"Provide a secret when principals change\");\n}","typeGuard":null,"tryCatchPattern":"try { return secret.with(s, scope); } catch (IllegalArgumentException e) { if (e.getMessage().equals(\"No secret available to encrypt\")) { /* supply a secret value or skip update */ } else throw e; }","preventionTips":["Never pass null secrets into principal-update flows","Model 'clear secret' as removing principals instead","Keep secret and principal updates in one consistent call"],"tags":["secrets","encryption","null-argument"],"backgroundTag":"null-argument","analyzedSha":"d85ca821baa46092a320ebb13546d7240adb74f8","analyzedAt":"2026-09-06T14:30:08.251Z","contentChangedAt":"2026-09-06T14:30:08.251Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}