passbolt/passbolt_api · error · ValidationException

Could not delete the resource.

Error message

Could not delete the resource.

What it means

A 422 ValidationException raised by _handleDeleteError as the final fallback when softDelete() failed with validation errors that match neither the soft-deleted nor has_access rules. The exception carries the resource entity and ResourcesTable errors so clients can see which fields failed validation.

Solutions

  1. Read the error details in the 422 response body — they name the failing field and rule.
  2. Run pending migrations and clear cache to fix schema/plugin drift.
  3. Disable recently added resource-related plugins to identify which rule fails.
  4. Fix the offending resource data directly (e.g. via cake console) if a row is corrupted.

Example fix

// before
const resp = await fetch(deleteUrl, {method: 'DELETE'});
throw new Error('delete failed');
// after
const resp = await fetch(deleteUrl, {method: 'DELETE'});
if (resp.status === 422) {
  const body = await resp.json();
  console.error('Validation errors:', body.errors); // field-level detail
}
Defensive patterns

Strategy: try-catch

Validate before calling

// surface the 422 error body to identify the failing field/rule before retrying
const resp = await fetch(`/resources/${id}.json`, {method: 'DELETE'});
if (resp.status === 422) { const {errors} = await resp.json(); inspect(errors); }

Try / catch

try { await api.delete(`/resources/${id}.json`); } catch (e) { if (e.status === 422) showFieldErrors(e.body.errors); else throw e; }

Prevention

When it happens

Trigger: DELETE /resources/{id}.json where a plugin-added or custom validation rule on the Resources entity fails during softDelete (any error key other than 'deleted.is_not_soft_deleted' or 'id.has_access').

Common situations: Custom plugins (e.g. metadata, folders) adding validation on resources, schema drift after an upgrade leaving fields in a state that fails rules, corrupted resource rows.

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/9b8f0129725cff92. Report an issue: GitHub.

Appendix: source

Thrown at src/Controller/Resources/ResourcesDeleteController.php:141

     */
    protected function _handleDeleteError(Resource $resource): void
    {
        $errors = $resource->getErrors();
        if (empty($errors)) {
            return;
        }
        if (isset($errors['deleted']['is_not_soft_deleted'])) {
            throw new NotFoundException(__('The resource does not exist.'));
        }
        if (isset($errors['id']['has_access'])) {
            // If the user has a read access return a 403, otherwise return a 404 to avoid data leak.
            $acoType = PermissionsTable::RESOURCE_ACO;
            if ($this->Resources->Permissions->hasAccess($acoType, $resource->id, $this->User->id())) {
                throw new ForbiddenException(__('You do not have the permission to delete this resource.'));
            }
            throw new NotFoundException(__('The resource does not exist.'));
        }
        throw new ValidationException(__('Could not delete the resource.'), $resource, $this->Resources);
    }

    /**
     * Send email notification
     *
     * @param \App\Model\Entity\Resource $resource Resource
     * @param \Cake\Datasource\ResultSetInterface $users Users who had access to the resource, deleter excluded
     * @return void
     */
    protected function _notifyUser(Resource $resource, ResultSetInterface $users): void
    {
        $event = new Event(static::DELETE_SUCCESS_EVENT_NAME, $this, [
            'resource' => $resource,
            'deletedBy' => $this->User->id(),
            'users' => $users,
        ]);
        $this->getEventManager()->dispatch($event);
    }

View on GitHub (pinned to 31c1bbc10f)