passbolt/passbolt_api · error · ValidationException

Could not validate resource data.

Error message

Could not validate resource data.

What it means

ResourcesUpdateService throws this ValidationException when the entity built from the request fails Passbolt's resource model validation rules. It is raised in handleValidationErrors (src/Service/Resources/ResourcesUpdateService.php:251) whenever $resource->getErrors() is non-empty after building the resource, wrapping the entity so the API response includes per-field validation details.

Solutions

  1. Inspect the `errors` body of the 400 response; it maps each invalid field to its validation rule message.
  2. Fix the offending field client-side (trim/shorten name, correct URI, provide required fields).
  3. Check the Resources table validation rules (src/Model/Table/ResourcesTable.php) for the exact constraint triggered.
  4. If rules changed after a Passbolt upgrade, update the client payload to the new schema.
  5. Run the update again once the payload conforms.

Example fix

// before
PATCH payload: {"name": ""}
// -> 400 Could not validate resource data (name required)
// after
PATCH payload: {"name": "Q1 Planning"}
Defensive patterns

Strategy: validation

Validate before calling

const RES_NAME_MAX = 255;
function validResourcePayload(payload) {
  const errors = {};
  if (!payload.name || !payload.name.trim()) errors.name = 'Name is required';
  if (payload.name && payload.name.length > RES_NAME_MAX) errors.name = 'Name too long';
  if (payload.uri && !/^([a-z][a-z0-9+.-]*):\/\//i.test(payload.uri)) errors.uri = 'Invalid URI scheme';
  return Object.keys(errors).length ? errors : null;
}
// call before PUT /resources/{id}; abort if it returns errors

Type guard

function isResourceDto(v) {
  return typeof v === 'object' && v !== null && typeof v.name === 'string'
    && (v.uri === undefined || typeof v.uri === 'string');
}

Try / catch

try {
  await api.updateResource(id, payload);
} catch (e) {
  if (e.status === 400 && e.body?.errors) {
    // surface e.body.errors per-field to the user
  } else throw e;
}

Prevention

When it happens

Trigger: PUT /resources/{id} with payload that violates resource rules: name/description constraints, expired metadata/key fields, invalid URI format, or schema violations on the resource entity during updateResource.

Common situations: Clients sending a name exceeding the DB column length, missing required fields when partial-update expectations are wrong, invalid resource URI (e.g. 'http://' scheme not allowed), or stale clients posting deprecated metadata fields after a schema migration.

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/4f2091f54f2122fb. Report an issue: GitHub.

Appendix: source

Thrown at src/Service/Resources/ResourcesUpdateService.php:251

            'secrets' => true,
        ]);

        return $this->Resources->patchEntity($resource, $data, $options);
    }

    /**
     * Handle resource validation errors.
     *
     * @param \App\Model\Entity\Resource $resource entity
     * @return void
     * @throws \App\Error\Exception\ValidationException
     * @throws \Cake\Http\Exception\NotFoundException
     */
    protected function handleValidationErrors(Resource $resource): void
    {
        $errors = $resource->getErrors();
        if (!empty($errors)) {
            throw new ValidationException(__('Could not validate resource data.'), $resource, $this->Resources);
        }
    }

    /**
     * Update the secrets.
     *
     * @param \App\Utility\UserAccessControl $uac The operator
     * @param \App\Model\Entity\Resource $resource The target resource
     * @param array $data The list of secrets to update
     * @return array
     * @throws \Exception If an unexpected error occurred
     */
    private function decorateSecretsData(UserAccessControl $uac, Resource $resource, array $data): array
    {
        $data = Hash::insert($data, '{n}.modified_by', $uac->getId());
        $data = Hash::insert($data, '{n}.created_by', $uac->getId());

        return $data;

View on GitHub (pinned to 31c1bbc10f)