passbolt/passbolt_api · error · InternalErrorException
The metadata private key cleartext data should be in JSON…
Error message
The metadata private key cleartext data should be in JSON format.
What it means
assertPrivateKey throws InternalErrorException when json_decode of the decrypted metadata private key cleartext fails (JSON_THROW_ON_ERROR). The decrypted data exists but is not valid JSON of depth <= 2, meaning the stored ciphertext does not decrypt to the expected JSON envelope (object containing object 'objectType', 'armored_key', etc.).
Solutions
- Enable debug to log the offending cleartext and inspect why it is not JSON
- Check the passbolt version that created this metadata private key; migrate/re-import keys created with an incompatible format
- Re-create or re-import the metadata private key so the stored data is the expected JSON envelope (json_encode of the cleartext DTO before encryption)
- Verify no double-encryption happened: the payload must decrypt exactly once to JSON
Example fix
// before: storing the armored key directly ciphertext = encrypt(armoredKey); // after: store the JSON envelope required by MetadataCleartextPrivateKeyForm ciphertext = encrypt(json_encode(['objectType' => 'PASSBOLT_METADATA_PRIVATE_KEY', 'armored_key' => $armoredKey, ...]));
Defensive patterns
Strategy: validation
Validate before calling
$decoded = json_decode($cleartext, true, 2);
if (json_last_error() !== JSON_ERROR_NONE || !is_array($decoded)) {
throw new \DomainException('Cleartext is not the expected JSON envelope.');
} Type guard
function isValidCleartextJson(?string $cleartext): bool {
$d = json_decode((string)$cleartext, true, 2);
return is_array($d) && !empty($d);
} Try / catch
try {
$service->shareMetadataKeysWithUser($uac, $userIds, $keyId);
} catch (MetadataKeyShareException $e) {
if (str_contains($e->getMessage(), 'should be in JSON format')) {
// cleartext corrupted/mis-encoded: re-import the metadata key
}
} Prevention
- Always json_encode the cleartext DTO before encrypting during import/migration
- Decrypt-once discipline: never store an already-encrypted payload as 'cleartext'
- Keep passbolt core and Metadata plugin versions aligned to avoid cleartext format drift
- Enable debug logging when importing keys to catch non-JSON cleartext early
When it happens
Trigger: shareMetadataKeyWithUser decrypts the server key copy and calls assertPrivateKey; if the plaintext is not parseable JSON (truncated data, double-encrypted payload, binary garbage, wrong version of the cleartext schema), this error is thrown with the raw cleartext logged in debug mode.
Common situations: Data written by an older passbolt version using a different cleartext format; ciphertext truncated during import/export; someone stored an armored key string directly instead of the JSON envelope.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- The metadata private key cleartext data should not be empty.
- The metadata private key should not be empty.
- Could not validate metadata key data.
- The metadata could not be encrypted with the metadata key…
- The metadata could not be encrypted with the metadata key id
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/e005ae4befc3f746.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltCe/Metadata/src/Service/MetadataKeyShareDefaultService.php:174
/**
* @param string $clearText private key object in json format
* @return void
*/
public function assertPrivateKey(string $clearText): void
{
if (empty($clearText)) {
$msg = __('The metadata private key should not be empty.');
throw new InternalErrorException($msg);
}
try {
$decoded = json_decode($clearText, true, 2, JSON_THROW_ON_ERROR);
} catch (Exception $exception) {
if (Configure::read('debug')) {
Log::error($clearText);
}
$msg = __('The metadata private key cleartext data should be in JSON format.');
throw new InternalErrorException($msg, 500, $exception);
}
if (!is_array($decoded) || empty($decoded)) {
$msg = __('The metadata private key cleartext data should not be empty.');
throw new InternalErrorException($msg);
}
$form = new MetadataCleartextPrivateKeyForm();
if (!$form->validate($decoded)) {
if (Configure::read('debug')) {
Log::error(json_encode($form->getErrors()));
}
$msg = __('The metadata private key cleartext data is not valid.');
throw new InternalErrorException($msg);
}
}
/**
* Get the OpenPGP Backend ready to decrypt with server keyView on GitHub (pinned to 31c1bbc10f)