passbolt/passbolt_api · error · BadRequestException
An array of arrays is expected.
Error message
An array of arrays is expected.
What it means
The bulk resource expiry update endpoint expects the payload to be an array of per-resource arrays (each containing id, expired, etc.). When any element of the submitted data is not an array (e.g. a scalar, string, or null), validateAndParsePayload rejects it with this BadRequestException before any persistence occurs.
Solutions
- Send each entry as an object with at least 'id' and 'expired' keys: data: [{"id":"<uuid>","expired":"2026-01-01"}]
- Validate the client payload shape before calling the endpoint (each item must be an object/associative array)
- Check the request Content-Type is application/json and the body is not being collapsed into a string
Example fix
// before
data: ["8e3874ae-4b40-590b-968a-418f70bdbb85"]
// after
data: [{"id": "8e3874ae-4b40-590b-968a-418f70bdbb85", "expired": null}] Defensive patterns
Strategy: validation
Validate before calling
if (!is_array($data) || array_filter($data, fn($r) => !is_array($r))) {
throw new \InvalidArgumentException('Each data item must be an array');
} Type guard
function isListOfArrays(mixed $data): bool {
return is_array($data) && array_all($data, fn($r) => is_array($r));
} Try / catch
try {
$service->updateMany($uac, $data);
} catch (BadRequestException $e) {
// log and return 400 with $e->getMessage()
} Prevention
- Validate payload shape client-side before sending
- Use typed request DTOs to deserialize the body
- Test the endpoint with malformed payloads in CI
When it happens
Trigger: PATCH/PUT to the resources expiry endpoint where the body's data array contains non-object elements, e.g. {"data": ["abc"]} or {"data": [null]}, instead of {"data": [{"id": "<uuid>", "expired": "..."}]}.
Common situations: Clients sending a flat list of UUID strings instead of objects; JSON encoding mistakes where objects become strings; scripting the API by hand and forgetting the per-resource wrapper.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- The entity must be an array.
- The metadata private key data must be an array.
- V4 related fields are not supported for V5.
- V4 related fields are not supported for V5.
- Account recovery case must be a string.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/357370fb8610fc16.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/PasswordExpiryPolicies/src/Service/Resources/PasswordExpiryPoliciesResourcesExpiryUpdateService.php:80
}
return $resources;
}
/**
* @param array $data payload
* @return array<string, \Cake\I18n\DateTime|null> array with the resourceIds as keys and the expiry date as value
* @throws \Cake\Http\Exception\BadRequestException if the expired value are not valid
* @throws \Cake\Http\Exception\BadRequestException if the resource_id value are not valid
* @throws \Cake\Http\Exception\BadRequestException if the resource_id value is found twice in the payload
* @throws \Cake\Http\Exception\BadRequestException if the sanitized array is empty
*/
protected function validateAndParsePayload(array $data): array
{
$dataSanitized = [];
foreach ($data as $resource) {
if (!is_array($resource)) {
throw new BadRequestException(__('An array of arrays is expected.'));
}
$resourceId = $resource['id'] ?? null;
if (!Validation::uuid($resourceId)) {
throw new BadRequestException(__('The identifier should be a valid UUID.'));
}
$isExpiredDefined = array_key_exists('expired', $resource);
if (!$isExpiredDefined) {
throw new BadRequestException(__('The expiry date is required.'));
}
$expiryDate = $resource['expired'];
if (array_key_exists($resourceId, $dataSanitized)) {
throw new BadRequestException(__('The identifier should be unique: {0}.', $resourceId));
}
$dataSanitized[$resourceId] = is_null($expiryDate) ? $expiryDate : new DateTime($expiryDate);
}
if (empty($dataSanitized)) {
throw new BadRequestException(__('The data should not be empty.'));
}View on GitHub (pinned to 31c1bbc10f)