xpipe-io/xpipe · error · BeaconClientException
Unable to parse or decrypt secret
Error message
Unable to parse or decrypt secret
What it means
SecretDecryptExchange deserializes the client-supplied encrypted secret via MultiPrincipalSecret.deserialize(). If deserialization yields null or the payload lacks an internal secret, the daemon cannot parse or decrypt the value and rejects the request. Typically the ciphertext was truncated, corrupted, encrypted for another daemon instance, or is not in the expected container format.
Source
Thrown at app/src/main/java/io/xpipe/app/beacon/api/SecretDecryptExchange.java:25
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
import tools.jackson.databind.JsonNode;
public class SecretDecryptExchange extends BeaconInterface<SecretDecryptExchange.Request> {
@Override
public String getPath() {
return "/secret/decrypt";
}
@Override
public Object handle(HttpExchange exchange, Request msg) throws BeaconClientException {
var secret = MultiPrincipalSecret.deserialize(msg.getEncrypted());
if (secret == null || secret.getInternalSecret() == null) {
throw new BeaconClientException("Unable to parse or decrypt secret");
}
return Response.builder()
.decrypted(new String(secret.getInternalSecret().getSecret()))
.build();
}
@Jacksonized
@Builder
@Value
public static class Request {
@NonNull
JsonNode encrypted;
}
@Jacksonized
@Builder
@ValueView on GitHub (pinned to d85ca821ba)
Solutions
- Re-obtain the encrypted secret from the original XPipe secret store rather than a copy
- Verify the daemon instance you call is the one that encrypted the secret (same key/vault)
- Confirm the value is sent as the full, unmodified base64 container (no truncation/whitespace)
- Upgrade client and daemon together if the serialization format changed
Example fix
// before
client.decryptSecret(userSuppliedString);
// after
if (userSuppliedString == null || userSuppliedString.isBlank()) {
throw new IllegalArgumentException("encrypted secret required");
}
client.decryptSecret(userSuppliedString.trim()); Defensive patterns
Strategy: validation
Validate before calling
if (encrypted == null || encrypted.isBlank()) {
throw new IllegalArgumentException("Encrypted secret payload is empty");
} Try / catch
try {
return client.decryptSecret(encrypted);
} catch (BeaconClientException e) {
if (e.getMessage().contains("Unable to parse or decrypt")) {
throw new IllegalStateException("Secret corrupt or encrypted by another daemon; re-encrypt it", e);
}
throw e;
} Prevention
- Only decrypt secrets produced by the same daemon instance/key
- Send the full unmodified base64 container (trim whitespace, never truncate)
- Keep client and daemon versions aligned on serialization format
When it happens
Trigger: Posting an encrypted secret blob that is null, empty, base64-corrupted, produced by a different XPipe installation's key, or wrapped in an unexpected multi-principal structure so getInternalSecret() returns null.
Common situations: Copying the XPipe secrets vault/key between machines; hand-editing or truncating stored secret strings; passing a plaintext value where an encrypted container is expected; version mismatch between client serialization format and daemon parser.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Unknown principal " + pr
- Principal " + p.getName() + " is not accessible
- Cannot delete category: " + cat.getName()
- Unsupported mode: " + msg.getMode().getDisplayName() + ". Su
- File path " + msg.getPath() + " is not absolute
AI-assisted analysis of xpipe-io/xpipe@d85ca821ba (2026-09-06).
Data as JSON: /api/errors/7ed707596be6ef7e.
Report an issue: GitHub.