passbolt/passbolt_api · error · BadRequestException
$e->getMessage() (from SubscriptionSignatureException)
Error message
$e->getMessage() (from SubscriptionSignatureException)
What it means
When EditionUpgradeService::upgrade() throws SubscriptionSignatureException (the subscription key's signature could not be verified), the controller rethrows it as BadRequestException carrying the original message. It means the supplied key fails cryptographic validation against passbolt's signing keys.
Solutions
- Re-copy the subscription key exactly as provided, without added whitespace or line breaks
- Verify the key is issued for this instance (correct domain/subscription id)
- Contact passbolt support to obtain a valid signed key
- Check the key file was not modified after download
Example fix
// before
$data = file_get_contents('key.asc'); // includes email quote wrapping -> invalid signature
// after
$data = trim(file_get_contents('key.asc')); // exact, unmodified key string Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check: key is an unmodified ASCII armored string, no extra quotes/line wrapping
if (preg_match('/[^[:print:][:space:]]/', $key)) { /* reject corrupted key */ } Type guard
function looksLikeSignedKey(string $s): bool { return str_contains($s, '-----BEGIN') && trim($s) === $s; } Try / catch
try {
await api.post('/edition/subscriptions', {data: key});
} catch (e) {
if (e.status === 400) { /* signature invalid: re-copy key exactly, verify with provider */ }
} Prevention
- Transfer key files as-is (no email/paste round-trips)
- Verify checksums of key files after download
- Confirm the key matches your instance domain/edition before import
When it happens
Trigger: Upgrading with a subscription key whose signature is invalid, tampered, corrupted in transit, or signed for a different edition/domain.
Common situations: Key file truncated or line-wrapped by copy/paste or email; key issued for a different passbolt instance or domain; using a CE key where PRO is required; expired or revoked key.
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.
Related errors
- The subscription key cannot be verified.
- The subscription key cannot be verified.
- A subscription key is already present.
- Decryption failed. Invalid signature. Expected
- $e->getMessage() (from SubscriptionException)
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/8420d005b7c7ea49.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltCe/Edition/src/Controller/EditionSubscriptionsCreateController.php:53
/**
* @return void
*/
public function create(): void
{
$this->User->assertIsAdmin();
$keyString = $this->getRequest()->getData('data');
if (!is_string($keyString) || trim($keyString) === '') {
throw new BadRequestException(__('Subscription key data is required.'));
}
$this->assertNotAlreadyPro();
try {
$keyDto = (new EditionUpgradeService())->upgrade($keyString, $this->User->getAccessControl());
} catch (SubscriptionSignatureException $e) {
throw new BadRequestException($e->getMessage());
} catch (SubscriptionException $e) {
throw new PaymentRequiredException($e->getMessage(), $e->getErrors());
}
$this->success(__('The subscription was created.'), $keyDto->toArray());
}
/**
* Rejects with HTTP 409 if the instance is already on PRO or already has a
* persisted subscription row.
*
* @return void
* @throws \Cake\Http\Exception\ConflictException
*/
private function assertNotAlreadyPro(): void
{
if ((new EditionGetService())->get()->isPro()) {
throw new ConflictException(__('The instance is already on PRO.'));View on GitHub (pinned to 31c1bbc10f)