passbolt/passbolt_api · error · InternalErrorException
Could not save permission history.
Error message
Could not save permission history.
What it means
Thrown by PermissionsHistoryTable::create() when CakePHP's save() returns false for a permission_history record that already passed validation. It signals an unexpected persistence failure (database constraint, connection, or afterSave rule) rather than bad input, so it surfaces as a 500 InternalErrorException.
Solutions
- Verify the referenced aco_foreign_key and aro_foreign_key exist in their parent tables before calling create()
- Run pending migrations (passbolt migrate) and confirm the permissions_history table exists with correct columns
- Check the database error log / CakePHP error.log for the underlying SQL failure and fix the constraint
- Re-run the permission change transaction; if transient (lock/timeout), retry after the DB recovers
Example fix
// before
$permissionHistory = $this->PermissionsHistory->create($data); // throws 500 on save failure
// after
$aco = $this->Aco->findById($data['aco_foreign_key'])->first();
$aro = $this->Aro->findById($data['aro_foreign_key'])->first();
if (!$aco || !$aro) {
throw new BadRequestException(__('Referenced aco/aro does not exist.'));
}
$permissionHistory = $this->PermissionsHistory->create($data); Defensive patterns
Strategy: try-catch
Validate before calling
use Cake\Validation\Validation; $ok = Validation::uuid($data['aco_foreign_key'] ?? '') && Validation::uuid($data['aro_foreign_key'] ?? '');
Type guard
if (!is_array($data) || empty($data['aco_foreign_key']) || empty($data['aro_foreign_key'])) { return false; } Try / catch
try {
$log = $this->PermissionsHistory->create($data);
} catch (\Cake\Http\Exception\InternalErrorException $e) {
$this->log('Permission history save failed: ' . $e->getMessage());
// inspect DB logs, retry or surface a 500 with context
} Prevention
- Always run migrations (passbolt migrate) after plugin upgrades
- Ensure aco/aro parent rows exist before writing permission history
- Wrap permission changes and their history writes in a DB transaction
- Monitor database error logs for constraint violations on permissions_history
When it happens
Trigger: Calling PermissionsHistoryTable::create($data) where the entity validates but $this->save($log) returns falsy — e.g. a database-level constraint violation (duplicate/missing aco_foreign_key or aro_foreign_key not present in aco/aro tables), storage engine failure, or an event listener aborting the save.
Common situations: Orphaned permission data after a hard delete (permissions referencing deleted resources/users), missing or broken permissions_history table after a skipped migration, database disk-full or lock issues, and plugin (Passbolt/Log) schema drift between versions.
Related errors
- Could not save the secret access.
- Could not save the secret history.
- Could not save the action.
- Could not save the action log.
- Could not save the comment, please try again later.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/b1cf6e29189567b8.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltCe/Log/src/Model/Table/PermissionsHistoryTable.php:212
*/
public function create(array $data): PermissionHistory
{
// Check validation rules.
$log = $this->buildEntity($data);
if ($log->getErrors()) {
throw new ValidationException(__('Could not validate permission history data.', true), $log, $this);
}
$permissionHistory = $this->save($log);
// Check for validation errors. (associated models too).
if ($log->getErrors()) {
throw new ValidationException(__('Could not validate permission history data.'), $permissionHistory, $this);
}
// Check for errors while saving.
if (!$permissionHistory) {
throw new InternalErrorException('Could not save permission history.');
}
return $permissionHistory;
}
}
View on GitHub (pinned to 31c1bbc10f)