passbolt/passbolt_api · error · Passbolt\Scim\Exception\ResourceNotFoundException

The resource with id ` ` is already deleted

Error message

The %s resource with id `%s` is already deleted

What it means

The user lookup intentionally includes soft-deleted users (findDeleted: true). If the matched user entity has deleted = true, setFromDatabase() throws ResourceNotFoundException with 'is already deleted' so that PATCH/PUT on a soft-deleted user fails clearly instead of silently resurrecting or modifying an inactive account.

Solutions

  1. Remove/deprovision the user on the IdP side so it stops syncing a deleted account.
  2. If the user should be active again, restore them in passbolt (undelete via users table/admin tooling) before applying SCIM operations.
  3. Recreate the user via POST /scim/v2/Users if a fresh account is desired.
  4. Catch ResourceNotFoundException on the client and treat it as a deprovision signal rather than retrying.
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check soft-delete state
$user = $usersTable->find()->where(['id' => $uuid])->first();
if ($user && $user->deleted) {
    throw new RuntimeException("User $uuid is soft-deleted; skipping SCIM operation");
}

Try / catch

try {
    $resource->setFromDatabase($uuid);
} catch (ResourceNotFoundException $e) {
    if (str_contains($e->getMessage(), 'is already deleted')) {
        // IdP should deprovision the user instead of patching
    }
}

Prevention

When it happens

Trigger: PATCH or PUT /scim/v2/Users/{uuid} where the UUID exists but the user row has deleted = true (user was deleted via passbolt UI/CLI and is in soft-delete state).

Common situations: IdP still has the user assigned to the passbolt app and keeps trying to sync attributes after an admin deleted the user; race between deprovisioning and attribute sync; restore-workflow tests hitting deleted fixtures.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at plugins/PassboltEe/Scim/src/Utility/Resource/UserScimResource.php:238

     */
    public function setFromDatabase(string $internalId): self
    {
        if (!Validation::uuid($internalId)) {
            throw new BadRequestException(__('The user identifier should be a valid UUID.'));
        }
        /** @var \App\Model\Entity\User|null $userEntity */
        $userEntity = $this->Users
            ->findForScim([$this->Users->aliasField('id') => $internalId], findDeleted: true)
            ->contain(['Profiles', 'ScimEntries'])
            ->first();
        $this->userEntity = $userEntity;
        if (!$this->userEntity) {
            throw new ResourceNotFoundException(
                sprintf('The %s resource with id `%s` was not found', $this->getType(), $internalId)
            );
        }
        if ($this->userEntity->deleted) {
            throw new ResourceNotFoundException(
                sprintf('The %s resource with id `%s` is already deleted', $this->getType(), $internalId)
            );
        }

        $this->id = $this->userEntity->id;
        $this->externalId = $this->userEntity->scim_entry?->external_identifier;
        $this->userName = $this->userEntity->scim_entry?->scim_name;
        $this->email = $this->userEntity->username;
        $this->firstName = $this->userEntity->profile?->first_name;
        $this->lastName = $this->userEntity->profile?->last_name;
        $this->active = !$this->userEntity->disabled;

        return $this;
    }

    /**
     * @inheritDoc
     */

View on GitHub (pinned to 31c1bbc10f)