passbolt/passbolt_api · error · BadRequestException
The metadata key is already shared with the user.
Error message
The metadata key is already shared with the user.
What it means
After existence checks, assertRequestSanity queries MetadataPrivateKeysTable for an existing private key matching the metadata key and target (user, or server when user_id is null). If one already exists, the duplicate share attempt is rejected with a BadRequestException.
Solutions
- Check for the existing metadata private key first (GET the key's private keys) and skip creation if present
- Make the client operation idempotent: treat this 400 as 'already done' where appropriate
- For re-sharing after rotation, delete/replace the old private key record or use the rotate flow instead of create
Example fix
// before
create($uac, $keyId, $data); // retried blindly
// after
if (!$privateKeysService->existsForUser($keyId, $userId)) {
create($uac, $keyId, $data);
} Defensive patterns
Strategy: try-catch
Validate before calling
const existing = await client.metadataPrivateKeys.list(keyId); if (existing.some(pk => pk.user_id === userId || pk.user_id === null)) return;
Type guard
const alreadyShared = (existing, userId) => existing.some(pk => (userId ? pk.user_id === userId : pk.user_id === null));
Try / catch
catch (e) { if (e.response?.status === 400 && /already shared/.test(e.response?.body?.message)) { return { status: 'already-shared' }; } throw e; } Prevention
- Make share operations idempotent by checking existing private keys first
- Serialize concurrent share operations per key
- Treat this 400 as success in retry loops
When it happens
Trigger: POSTing a metadata private key for a user (or the server) that already has one for the same metadata key id — including retries of a successful earlier call.
Common situations: Non-idempotent retry logic resending the same share; two admins sharing the same key concurrently; client not updating its local copy of who has the key.
Related errors
- Few fields are missing for the V5.
- Folder ID " " is already V5
- The metadata key is already marked as expired.
- The server metadata private key is required to enable these…
- V4 related fields are not supported for V5.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/e385d1d94af15ca9.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltCe/Metadata/src/Service/MetadataPrivateKeysCreateService.php:156
} catch (RecordNotFoundException $exception) {
throw new NotFoundException(__('The metadata key does not exist or has been deleted.'));
}
// Assert private key does not already exist for the user/server
/** @var \Passbolt\Metadata\Model\Table\MetadataPrivateKeysTable $metadataPrivateKeysTable */
$metadataPrivateKeysTable = $this->fetchTable('Passbolt/Metadata.MetadataPrivateKeys');
$metadataPrivateKey = $metadataPrivateKeysTable->find()
->where(['metadata_key_id' => $metadataKeyId])
->where(function (QueryExpression $exp) use ($data) {
if (isset($data['user_id'])) {
return $exp->eq('user_id', $data['user_id']);
}
return $exp->isNull('user_id');
})
->first();
if (!empty($metadataPrivateKey)) {
throw new BadRequestException(__('The metadata key is already shared with the user.'));
}
}
/**
* @param \App\Utility\UserAccessControl $uac User access control.
* @param \Passbolt\Metadata\Model\Dto\MetadataPrivateKeysCreateManyDto $dto User provided data.
* @return void
* @throws \Cake\Http\Exception\BadRequestException if the data is invalid
* @throws \App\Error\Exception\ValidationException if the data does not validate
* @throws \Cake\Http\Exception\InternalErrorException if data could not be saved because of an internal issue
* @throws \Cake\Http\Exception\NotFoundException if the key was not found
*/
public function createMany(UserAccessControl $uac, MetadataPrivateKeysCreateManyDto $dto): void
{
$uac->assertIsAdmin();
if (empty($dto->getData())) {
return;
}View on GitHub (pinned to 31c1bbc10f)