laravel/framework · critical · DecryptException
Could not decrypt the data.
Error message
Could not decrypt the data.
What it means
After MAC validation (or if MAC validation is disabled), Encrypter calls openssl_decrypt; if it returns false, DecryptException('Could not decrypt the data.') is thrown. Unlike the MAC failure this means the payload passed structural and integrity checks but the cipher operation itself failed — typically because the IV/tag length is wrong for the algorithm or the value was corrupted in a way that still passes the MAC check (extremely rare) or because the cipher/key pair is inconsistent with the one that produced the value.
Solutions
- Ensure the cipher configuration at decrypt time matches what was used to encrypt (config('app.cipher') consistent across environments).
- Store/transport ciphertext in base64 form within binary-safe columns and cookies; avoid collation that re-encodes the bytes.
- If you recently changed ciphers, re-encrypt all existing values using the previous cipher before switching.
- Verify the payload length and IV/tag byte lengths are intact end-to-end.
Example fix
// before // data encrypted under aes-256-gcm, now decrypted under aes-256-cbc config // after — keep cipher consistent end-to-end // config/app.php (both environments): 'cipher' => 'aes-256-gcm', // re-encrypt legacy payloads during a one-time migration if changing cipher
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure cipher consistency between encrypt and decrypt environments
if (config('app.cipher') !== $expectedCipher) {
throw new RuntimeException('Cipher mismatch — cannot safely decrypt.');
} Try / catch
use Illuminate\Contracts\Encryption\DecryptException;
try {
return Crypt::decrypt($payload);
} catch (DecryptException $e) {
if (str_contains($e->getMessage(), 'Could not decrypt')) {
// likely cipher/IV/tag corruption — log and treat as invalid
report($e);
return null;
}
throw $e;
} Prevention
- Keep app.cipher identical across all environments that exchange encrypted data.
- Store ciphertext in binary-safe columns (BLOB / VARBINARY) or base64-encoded text.
- Re-encrypt existing values when changing cipher rather than decrypting across versions.
When it happens
Trigger: Calling Crypt::decrypt() with a valid MAC but an IV/tag that openssl_decrypt rejects; mismatched cipher between encrypt and decrypt (e.g. data encrypted as aes-256-gcm then decrypted with aes-256-cbc configuration); corrupted IV/tag bytes from cookie truncation.
Common situations: Changing app.cipher between encrypt and decrypt without re-encrypting; proxy/CDN stripping bytes from the payload; PHP/OpenSSL version differences between the encrypting system and the decrypting system; corrupted stored ciphertext in a database column with wrong collation.
Related errors
- Could not encrypt the data.
- The MAC is invalid.
- The payload is invalid.
- Unsupported cipher or incorrect key length. Supported…
- Could not verify the hashed value's configuration.
AI-assisted analysis of laravel/framework@e0f6eb3518 (2026-08-11).
Data as JSON: /api/errors/b66acdda3ebbd951.
Report an issue: GitHub.
Appendix: source
Thrown at src/Illuminate/Encryption/Encrypter.php:201
);
if ($decrypted !== false) {
break;
}
}
if ($this->shouldValidateMac() && $validKey === null) {
throw new DecryptException('The MAC is invalid.');
}
if ($this->shouldValidateMac()) {
$decrypted = \openssl_decrypt(
$payload['value'], strtolower($this->cipher), $validKey, 0, $iv, $tag ?? ''
);
}
if (($decrypted ?? false) === false) {
throw new DecryptException('Could not decrypt the data.');
}
return $unserialize ? unserialize($decrypted) : $decrypted;
}
/**
* Decrypt the given string without unserialization.
*
* @param string $payload
* @return string
*
* @throws \Illuminate\Contracts\Encryption\DecryptException
*/
public function decryptString($payload)
{
return $this->decrypt($payload, false);
}
View on GitHub (pinned to e0f6eb3518)