{"record":{"id":"f777734ebd1d1b4e","repo":"passbolt/passbolt_api","slug":"the-subscription-key-cannot-be-verified","errorCode":null,"errorMessage":"The subscription key cannot be verified.","messagePattern":"The subscription key cannot be verified\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"plugins/PassboltEe/Subscription/src/Controller/Subscriptions/SubscriptionsUpdateController.php","lineNumber":49,"sourceCode":"{\n    /**\n     * @return void\n     */\n    public function update(): void\n    {\n        if (!$this->User->isAdmin()) {\n            throw new ForbiddenException(__('You are not allowed to access this location.'));\n        }\n\n        $keyString = $this->getRequest()->getData('data');\n        if (!is_string($keyString) || trim($keyString) === '') {\n            throw new BadRequestException(__('Subscription key data is required.'));\n        }\n\n        try {\n            $keyDto = (new SubscriptionKeySaveService())->save($keyString, $this->User->getAccessControl());\n        } catch (SubscriptionSignatureException $e) {\n            throw new BadRequestException($e->getMessage());\n        } catch (SubscriptionException $e) {\n            throw new PaymentRequiredException($e->getMessage(), $e->getErrors());\n        }\n\n        // POST and PUT both land here for backwards compatibility.\n        // Preserve the historical success messages of the now-deleted\n        // SubscriptionsCreateController (POST) and this controller (PUT) so the\n        // legacy SubscriptionsCreateControllerTest keeps passing unchanged.\n        $message = $this->getRequest()->is('post')\n            ? __('The subscription was created.')\n            : __('The subscription was updated.');\n\n        $this->success($message, $keyDto->toArray());\n    }\n}\n","sourceCodeStart":31,"sourceCodeEnd":65,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/Subscription/src/Controller/Subscriptions/SubscriptionsUpdateController.php#L31-L65","documentation":"SubscriptionKeySaveService::save threw SubscriptionSignatureException, meaning the submitted subscription key's cryptographic signature failed verification — the key is invalid, corrupted, or not a genuine passbolt-issued license. The controller surfaces it as a 400 with this message.","triggerScenarios":"POST/PUT /subscription.jsonapi where the key text was truncated, line-wrapped/altered in transit, hand-edited, or simply not a valid passbolt subscription key.","commonSituations":"Copy/paste losing PGP message lines; email client re-wrapping the armored key; pasting a key for a different product or customer; attempting to forge/modify an existing key.","solutions":["Re-copy the subscription key exactly as delivered, including the full PGP MESSAGE armor blocks","Re-download the key from the passbolt customer portal","Confirm the key matches your subscription/organization ID and is for the passbolt product","Contact passbolt support if a freshly issued key still fails signature verification"],"exampleFix":"// before\ndata=-----BEGIN PGP MESSAGE-----\n...truncated block...\n// after\ndata=-----BEGIN PGP MESSAGE-----\n<full, unmodified armored subscription key>\n-----END PGP MESSAGE-----","handlingStrategy":"validation","validationCode":"const key = fs.readFileSync('subscription_key.txt', 'utf8').trim();\nif (!key.startsWith('-----BEGIN PGP MESSAGE-----') || !key.endsWith('-----END PGP MESSAGE-----')) {\n  throw new Error('Subscription key armor incomplete — re-copy the full key');\n}","typeGuard":null,"tryCatchPattern":"const res = await saveKey(key);\nif (res.status === 400 && res.body.message.includes('cannot be verified')) {\n  console.error('Key signature invalid: re-download the key and retry without editing it');\n}","preventionTips":["Never hand-edit the armored key text","Disable line re-wrapping in the client used to copy the key","Keep the original email/portal copy as the single source of truth"],"tags":["subscription","signature","license","crypto","bad-request"],"backgroundTag":"checksum-mismatch","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T09:17:21.228Z"}