passbolt/passbolt_api · error · BadRequestException

Subscription key data is required.

Error message

Subscription key data is required.

What it means

The controller requires a non-empty string in the 'data' form field containing the subscription key. When the field is missing, not a string, or whitespace-only, a 400 is thrown before any save attempt.

Solutions

  1. Send the subscription key as form field data=<key string>
  2. Ensure the key string is copied completely including BEGIN/END markers
  3. If sending JSON, use the expected form-encoded body the controller reads via getData('data')
  4. Verify the Content-Type is application/x-www-form-urlencoded (or multipart) so CakePHP parses the field

Example fix

// before
curl -X POST .../subscription.jsonapi -H "Authorization: ..."  # no body
// after
curl -X POST .../subscription.jsonapi -H "Authorization: ..." -d "data=-----BEGIN PGP MESSAGE----- ... -----END PGP MESSAGE-----"
Defensive patterns

Strategy: validation

Validate before calling

const key = subscriptionKey.trim();
if (!key) throw new Error('Subscription key data is required');
const body = new URLSearchParams({data: subscriptionKey});
// then send body as application/x-www-form-urlencoded

Type guard

function hasSubscriptionKey(v) { return typeof v === 'string' && v.trim() !== ''; }

Try / catch

if (res.status === 400 && body.message.includes('Subscription key data is required')) {
  console.error('Send the key in the form field "data"');
}

Prevention

When it happens

Trigger: POST /subscription.jsonapi without the data field, with data=, or with data=" "; sending JSON body instead of form data so getData('data') returns null.

Common situations: Scripts forgetting the -d "data=..." form field; copying an empty key from email; curl sending multipart without the field name matching exactly 'data'.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/13cf2aafa68b2c13. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltEe/Subscription/src/Controller/Subscriptions/SubscriptionsUpdateController.php:43

use Passbolt\Subscription\Service\Subscriptions\SubscriptionKeySaveService;

/**
 * Class SubscriptionsUpdateController
 */
class SubscriptionsUpdateController extends AppController
{
    /**
     * @return void
     */
    public function update(): void
    {
        if (!$this->User->isAdmin()) {
            throw new ForbiddenException(__('You are not allowed to access this location.'));
        }

        $keyString = $this->getRequest()->getData('data');
        if (!is_string($keyString) || trim($keyString) === '') {
            throw new BadRequestException(__('Subscription key data is required.'));
        }

        try {
            $keyDto = (new SubscriptionKeySaveService())->save($keyString, $this->User->getAccessControl());
        } catch (SubscriptionSignatureException $e) {
            throw new BadRequestException($e->getMessage());
        } catch (SubscriptionException $e) {
            throw new PaymentRequiredException($e->getMessage(), $e->getErrors());
        }

        // POST and PUT both land here for backwards compatibility.
        // Preserve the historical success messages of the now-deleted
        // SubscriptionsCreateController (POST) and this controller (PUT) so the
        // legacy SubscriptionsCreateControllerTest keeps passing unchanged.
        $message = $this->getRequest()->is('post')
            ? __('The subscription was created.')
            : __('The subscription was updated.');

View on GitHub (pinned to 31c1bbc10f)