laravel/framework · error · DecryptException
Could not decrypt the data.
Error message
Could not decrypt the data.
What it means
decrypt() throws DecryptException('Could not decrypt the data.') when openssl_decrypt() ultimately returns false — i.e. neither the current nor any previous key successfully decrypts the ciphertext. Also thrown by ensureTagIsValid() when an AEAD tag is the wrong length (16 bytes required). Distinguish from [256]: this fires when the MAC (if checked) was OK but decryption still failed, or for AEAD ciphers where the tag/auth failed.
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 bd6b5437e6)
Solutions
- Verify the payload is transmitted byte-exact: use rawurlencode/rawurldecode for URLs, store as BLOB/VARBINARY not as lossy VARCHAR without a proper collation.
- Confirm cipher hasn't changed since the value was encrypted; re-encrypt existing data after cipher migrations.
- For AEAD, ensure the tag field is present and 16 bytes after base64_decode; ensureTagIsValid enforces this.
- Log the payload length and base64-decodability to isolate corruption from key mismatch.
Example fix
// before — encrypted value stored in a charset-lossy column
// migrations: $table->string('token'); // collation truncates bytes
// after — store as binary
$table->binary('token');
// and round-trip via the encrypter untouched:
$stored = encrypt($value);
$back = decrypt($stored); Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure byte-exact transport and storage
$raw = $request->input('token');
if (is_string($raw) && \Illuminate\Encryption\Encrypter::appearsEncrypted($raw)) {
return decrypt($raw);
} Try / catch
try {
$value = decrypt($payload);
} catch (\Illuminate\Contracts\Encryption\DecryptException $e) {
// log length/base64 validity; treat as corrupted/untrusted
logger()->warning('decrypt failed', ['len' => strlen($payload ?? '')]);
$value = null;
} Prevention
- Store encrypted values in binary-safe columns (BLOB/VARBINARY).
- URL-transport encrypted values with rawurlencode/rawurldecode.
- Do not change cipher without re-encrypting; preserve base64 padding in transit.
When it happens
Trigger: Corrupted ciphertext/IV/tag in the payload; truncated base64; payload encrypted with an algorithm different from the current cipher; AEAD tag missing or wrong length; an encrypted value passed through a transport that mangled bytes (URL encoding, charset conversion).
Common situations: Cookies mangled by proxies/CDNs; encrypted query params URL-decoded incorrectly; switching from CBC to GCM (or vice versa) without re-encrypting; truncated database columns holding encrypted values; copy-paste truncation of long base64.
Related errors
- Could not encrypt the data.
- The MAC is invalid.
- The payload is invalid.
- Unable to use tag because the cipher algorithm does not supp
- The provided class must extend [{Collection::class}].
AI-assisted analysis of laravel/framework@bd6b5437e6 (2026-08-06).
Data as JSON: /data/errors/b66acdda3ebbd951.json.
Report an issue: GitHub.