passbolt/passbolt_api · error · ValidationException

Could not validate the transfer data.

Error message

Could not validate the transfer data.

What it means

A ValidationException thrown by TransfersUpdateService::update when the patched Transfer entity fails field validation BEFORE any save is attempted, and additionally after assertTransitionAllowed() checks the status transition. It carries the entity and table so per-field errors are available to callers.

Solutions

  1. Inspect the exception's entity errors for the exact failing field
  2. Send current_page as a 0-based integer strictly less than total_pages
  3. Use exact status values: start, in_progress, complete, cancel
  4. Refetch the transfer state before patching to avoid patching a transfer modified elsewhere
  5. Do not patch fields that are not part of the update whitelist

Example fix

// before
patch(['current_page' => '3', 'status' => 'completed']); // string page, wrong status
// after
patch(['current_page' => 2, 'status' => 'complete']); // 0-based int < total_pages
Defensive patterns

Strategy: validation

Validate before calling

if (!Number.isInteger(p.current_page) || p.current_page < 0 || p.current_page >= p.total_pages) throw new Error('current_page must be a 0-based int < total_pages');
if (!['in_progress','complete','cancel'].includes(p.status)) throw new Error('invalid update status');

Try / catch

try { return await patchTransfer(id, payload); } catch (e) { if (e.status === 400 && e.body?.errors) { syncFromServerAndReapply(); } throw e; }

Prevention

When it happens

Trigger: PATCH/PUT /mobile/transfers/<id> with payload fields that fail validation — e.g. non-numeric current_page, negative page numbers, invalid status string, or a patch that leaves the entity in an inconsistent state (current_page > total_pages).

Common situations: Client sending pages as strings or 1-based indexes when the API expects 0-based; sending an off-by-one current_page greater than or equal to total_pages; status typos like 'completed' instead of 'complete'; stale client state after another device cancelled the transfer.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at plugins/PassboltCe/Mobile/src/Service/Transfers/TransfersUpdateService.php:87

     * @param \Passbolt\Mobile\Model\Entity\Transfer $transfer entity
     * @param array $data entity data
     * @param \App\Utility\UserAccessControl $uac user access control object
     * @throws \App\Error\Exception\ValidationException if data do not validate
     * @throws \Cake\Http\Exception\InternalErrorException if saving data is not possible
     * @return \Passbolt\Mobile\Model\Entity\Transfer
     */
    public function update(Transfer $transfer, array $data, UserAccessControl $uac): Transfer
    {
        $this->assertOperationIsAllowed($transfer, $uac);

        // Check for validation errors
        $originalTransfer = clone $transfer;
        $transfer = $this->patchTransferEntity($transfer, $data);
        $this->assertTransitionAllowed($originalTransfer, $transfer);

        if ($transfer->getErrors()) {
            $msg = __('Could not validate the transfer data.');
            throw new ValidationException($msg, $transfer, $this->Transfers);
        }

        // Save and check for application rules errors.
        $transferSaved = $this->Transfers->save($transfer);
        if ($transfer->getErrors()) {
            $msg = __('Could not update the transfer data.');
            throw new ValidationException($msg, $transfer, $this->Transfers);
        }

        // Check for errors while saving.
        if (!$transferSaved) {
            throw new InternalErrorException(__('The transfer could not be updated.'));
        }

        return $transfer;
    }

    /**

View on GitHub (pinned to 31c1bbc10f)