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

  1. Send each entry as an object with at least 'id' and 'expired' keys: data: [{"id":"<uuid>","expired":"2026-01-01"}]
  2. Validate the client payload shape before calling the endpoint (each item must be an object/associative array)
  3. 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

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


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)