passbolt/passbolt_api · error · BadRequestException
The entity must be an array.
Error message
The entity must be an array.
What it means
MetadataBatchUpdateValidationService::validateMany processes batch metadata update payloads and requires every entry in the request array to be an associative array (an entity object with at least an `id`). If any element is a scalar or null, it throws this BadRequestException. It is the first guard of the batch update validation pipeline.
Solutions
- Send each batch entry as an object containing at least the `id` key: [{"id": "<uuid>", ...}, ...].
- Validate the payload shape client-side, ensuring every element is_array before the request.
- Fix JSON serialization so no element collapses to a scalar/null (e.g. empty objects encoded as empty arrays or null).
- Wrap validateMany and return a 400 identifying the offending index to callers.
Example fix
// before
["<uuid-1>", "<uuid-2>"]
// after
[{"id": "<uuid-1>", "metadata_key_id": "<key-uuid>"}, {"id": "<uuid-2>", "metadata_key_id": "<key-uuid>"}] Defensive patterns
Strategy: validation
Validate before calling
if (!is_array($batch)) {
throw new InvalidArgumentException('Batch body must be a list');
}
foreach ($batch as $i => $entry) {
if (!is_array($entry) || !isset($entry['id'])) {
throw new InvalidArgumentException("Batch entry $i must be an object with an id");
}
} Type guard
function isBatchEntityList(mixed $v): bool {
return is_array($v) && array_reduce($v, fn($ok, $e) => $ok && is_array($e) && isset($e['id']), true);
} Try / catch
try {
$data = $service->validateMany($requestData);
} catch (BadRequestException $e) {
return $this->getResponse()->withStatus(400, 'Each batch entry must be an object');
} Prevention
- Send objects, not bare ID strings, in batch update payloads
- Guard against JSON nulls/empty arrays entering the batch list
- Add a schema check on the client that rejects scalar batch elements
- Log the offending element index server-side for easier debugging
When it happens
Trigger: Batch metadata update endpoints (e.g. PUT /metadata/resources or /metadata/folders batch) receiving a request array where an element is a string/number/null instead of an object like {"id": "<uuid>", ...}; calling validateMany directly with malformed arrays.
Common situations: Clients sending a flat list of ID strings instead of objects; JSON payloads where one row was serialized incorrectly; automation scripts building the batch list by joining IDs rather than objects.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- The metadata private key data must be an array.
- An array of arrays is expected.
- The identifier should be a valid UUID.
- V4 related fields are not supported for V5.
- V4 related fields are not supported for V5.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/44cfcaaeafaae119.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltCe/Metadata/src/Model/Validation/MetadataBatchUpdateValidationService.php:57
/**
* Get the validation form
*
* @return \Passbolt\Metadata\Form\Upgrade\MetadataBatchUpgradeForm
*/
abstract public function getForm(): MetadataBatchUpgradeForm;
/**
* @param array $requestData Request data.
* @return array
* @throws \Cake\Http\Exception\BadRequestException If data is invalid.
* @throws \App\Error\Exception\CustomValidationException If data is invalid.
* @throws \Cake\Http\Exception\NotFoundException If one or more resources are not found.
*/
public function validateMany(array $requestData): array
{
foreach ($requestData as $values) {
if (!is_array($values)) {
throw new BadRequestException(__('The entity must be an array.'));
}
$id = $values['id'] ?? null;
if (!Validation::uuid($id)) {
throw new BadRequestException(__('The identifier should be a valid UUID.'));
}
}
$entityIds = Hash::extract($requestData, '{n}.id');
$this->entities = $this->queryEntitiesFromIds($entityIds)->all()->toArray();
// Re-arrange entities array to set key as identifier and value as entity object to easily find it
$this->entities = Hash::combine($this->entities, '{n}.id', '{n}');
$data = [];
$errors = [];
foreach ($requestData as $i => $entity) {
$entityId = $entity['id'];
if (!array_key_exists($entityId, $this->entities)) {View on GitHub (pinned to 31c1bbc10f)