passbolt/passbolt_api · error · InternalErrorException
Folder ID " " is already V5
Error message
Folder ID "{0}" is already V5 What it means
During bulk V4-to-V5 folder migration, each folder is converted to a MetadataFolderDto and checked; if the DTO already reports isV5() (metadata populated, name nulled), an InternalErrorException is thrown because migrating an already-migrated folder is an invariant violation in the batch. The message includes the folder ID to locate the offender.
Solutions
- Exclude already-v5 folders from the query (add a condition on metadata IS NOT NULL / name IS NULL).
- Skip-and-log already-migrated folders instead of throwing, to make the migration idempotent.
- Re-run the migration only after confirming no partial state: check folders where metadata is set but name is not null.
- Ensure only one migration job runs at a time (use a lock) to avoid double processing.
Example fix
// before: select all folders, throw on already-v5
$folders = $foldersTable->find()->contain('Permissions')->all();
// after: filter at query time
$folders = $foldersTable->find()
->where(['metadata IS' => null])
->contain('Permissions')->all(); Defensive patterns
Strategy: validation
Validate before calling
$remaining = TableRegistry::getTableLocator()->get('Folders')
->find()->where(['metadata IS' => null])->count();
if ($remaining === 0) { /* nothing to migrate, skip the job */ } Type guard
if ($dto->isV5()) { continue; } // skip already-migrated folders Try / catch
try {
$service->migrate($uac);
} catch (\Cake\Http\Exception\InternalErrorException $e) {
if (str_contains($e->getMessage(), 'is already V5')) {
// exclude that folder id and resume migration
}
} Prevention
- Make the candidate query exclude already-v5 folders so runs are idempotent
- Use a lock so only one migration job runs at a time
- Track migration progress (folder IDs done) to allow safe resume
When it happens
Trigger: Running the migrate-all-folders command/service on a database where some folders were already migrated to v5 (partially completed earlier run, or folders migrated manually/via another job) — the query selecting candidate folders matched them again.
Common situations: Re-running a failed migration job without cleaning up; concurrent migration workers double-processing folders; snapshot/restore mixing pre- and post-migration data.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Folder creation with cleartext metadata not allowed.
- The metadata could not be encrypted with the user id: .
- Few fields are missing for the V5.
- Folder can not be shared
- Folder creation/modification with encrypted metadata not…
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/6a6e5c4a4929252b.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltCe/Metadata/src/Service/Migration/MigrateAllV4FoldersToV5Service.php:100
->find()
->contain(['Permissions.Users.Gpgkeys'])
->where(['name IS NOT NULL'])
->all()
->toArray();
if (empty($folders)) {
$this->addError(['error_message' => __('No folders to migrate.')]);
return $this->getResult();
}
foreach ($folders as $folder) {
$dto = MetadataFolderDto::fromArray($folder->toArray());
try {
if ($dto->isV5()) {
$msg = __('Folder ID "{0}" is already V5', $folder->id);
throw new InternalErrorException($msg);
}
if (count($folder->permissions) === 0) {
$msg = __('No permission found for folder ID {0}', $folder->id);
throw new InternalErrorException($msg);
}
if (count($folder->permissions) === 1) {
$this->migratePersonal($dto, $folder);
} else {
$this->migrateShared($dto, $folder);
}
$this->addMigrated($folder);
} catch (Exception $e) {
// Continue with next resource if any error
$error = ['folder_id' => $folder->id, 'error_message' => $e->getMessage()];
if (Configure::read('debug')) {
$error['trace'] = $e->getTraceAsString();
}View on GitHub (pinned to 31c1bbc10f)