passbolt/passbolt_api · error · ValidationException

Could not update the authentication token data.

Error message

Could not update the authentication token data.

What it means

Thrown by RecoverCompleteService::complete when saving the freshly built authentication token entity back to the authentication_tokens table fails entity validation. The token was already consumed in memory, so a failed save leaves the recovery in an inconsistent state and surfaces as a ValidationException.

Solutions

  1. Run pending migrations (ddev refresh / cake migrations migrate) so authentication_tokens matches the expected schema
  2. Retry the recover-complete request once the database is healthy; if it persists, inspect the entity errors via the ValidationException detail
  3. Check the authentication_tokens row for the token id and fix or remove the corrupted row
  4. Verify database connectivity and that no migration lock is held
Defensive patterns

Strategy: retry

Validate before calling

// ensure migrations are current before the flow
await ensureMigrationsUpToDate(); // cake migrations status / migrate

Try / catch

try { await recoverComplete(userId, token, key); }
catch (e) { if (isTokenSaveValidation(e)) { await retryWithBackoff(() => recoverComplete(...), 1); } else throw e; }

Prevention

When it happens

Trigger: Database-level validation failure on the token entity during update — e.g. schema constraint mismatch, unexpected field state, or a database connectivity/lock issue surfacing as a save error.

Common situations: Database schema drift after an incomplete migration; MariaDB/Postgres-specific column incompatibility; concurrent writes locking the row; corrupted token row in authentication_tokens.

Understand the failure class

Related errors


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

Appendix: source

Thrown at src/Service/Setup/RecoverCompleteService.php:54

     * @throws \Cake\Http\Exception\BadRequestException if no authentication token was provided
     * @throws \Cake\Http\Exception\BadRequestException if the authentication token is not a uuid
     * @throws \Cake\Http\Exception\BadRequestException if the authentication token is expired or invalid
     * @throws \Cake\Http\Exception\BadRequestException if the OpenPGP key is not provided or not a valid OpenPGP key
     * @throws \Cake\Http\Exception\BadRequestException if the OpenPGP key does not belong to the user
     * @throws \App\Error\Exception\CustomValidationException if the token was already consumed by a concurrent request
     * @param string $userId uuid of the user
     * @return void
     */
    public function complete(string $userId): void
    {
        $this->AuthenticationTokens->getConnection()->transactional(function () use ($userId): void {
            $user = $this->validateData($userId);
            $token = $this->buildAuthenticationTokenEntity($userId);

            $this->consumeTokenOrFail($token);

            if (!$this->AuthenticationTokens->save($token)) {
                throw new ValidationException(
                    __('Could not update the authentication token data.'),
                    $token,
                    $this->AuthenticationTokens
                );
            }

            $this->dispatchEvent(RecoverCompleteServiceInterface::COMPLETE_SUCCESS_EVENT_NAME, [
                'user' => $user,
                'data' => $this->request->getData(),
                'clientIp' => $this->request->clientIp(),
                'userAgent' => $this->request->getEnv('HTTP_USER_AGENT'),
            ]);
        });
    }

    /**
     * Extension seam: subclasses may attach associations that `complete()` will cascade-save.
     *

View on GitHub (pinned to 31c1bbc10f)