passbolt/passbolt_api · error · BadRequestException
The expiry date is required.
Error message
The expiry date is required.
What it means
Each entry in the bulk expiry update payload must contain an 'expired' key (the value may be null to clear the expiry date). validateAndParsePayload throws this BadRequestException when the 'expired' key is entirely absent from an item, since the operation cannot know the intended expiry state.
Solutions
- Always include 'expired' in each item — use null explicitly to clear the expiry date
- Pass a valid date string (e.g. '2026-12-31T00:00:00+00:00') to set an expiry
- Update the client/integration to the current API contract for this endpoint
Example fix
// before
data: [{"id": "8e3874ae-4b40-590b-968a-418f70bdbb85"}]
// after
data: [{"id": "8e3874ae-4b40-590b-968a-418f70bdbb85", "expired": null}] // or a date Defensive patterns
Strategy: validation
Validate before calling
foreach ($data as $i => $item) {
if (!array_key_exists('expired', $item)) {
throw new \InvalidArgumentException("Item $i missing required 'expired' key");
}
} Type guard
function hasExpiredKey(array $item): bool {
return array_key_exists('expired', $item);
} Try / catch
try {
$service->updateMany($uac, $data);
} catch (BadRequestException $e) {
// inspect payload for missing 'expired' keys before retry
} Prevention
- Always send the 'expired' key, even when clearing the date with null
- Document the payload contract for API consumers
- Add schema validation (JSON schema) on the client payload
When it happens
Trigger: Submitting data items like {"id": "<uuid>"} without the 'expired' key to the resources expiry update endpoint.
Common situations: Clients assuming omitting 'expired' means 'leave unchanged' (the API requires the key even to clear the date with null); older client versions predating the expired field requirement.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Invalid status.
- The identifier should be a valid UUID.
- The identifier should be a valid UUID.
- The SSO setting id should be a uuid.
- The SSO setting id should be a uuid.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/0b58c1108b3b7282.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/PasswordExpiryPolicies/src/Service/Resources/PasswordExpiryPoliciesResourcesExpiryUpdateService.php:88
* @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.'));
}
return $dataSanitized;
}
/**
* @param \App\Utility\UserAccessControl $uac UAC
* @param array $resourceIds the list of the resourceIds to update
* @return voidView on GitHub (pinned to 31c1bbc10f)