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

  1. Ensure the cipher configuration at decrypt time matches what was used to encrypt (config('app.cipher') consistent across environments).
  2. Store/transport ciphertext in base64 form within binary-safe columns and cookies; avoid collation that re-encodes the bytes.
  3. If you recently changed ciphers, re-encrypt all existing values using the previous cipher before switching.
  4. 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

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


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)