laravel/framework · critical · DecryptException

The MAC is invalid.

Error message

The MAC is invalid.

What it means

During decryption, Encrypter iterates over the current and any previous keys, recomputing the MAC for each; if shouldValidateMac() is true and no key produced a matching MAC ($validKey === null), it throws DecryptException('The MAC is invalid.'). This is the canonical 'wrong key' or 'tampered payload' failure — the payload parsed fine but its MAC does not validate against any available key.

Solutions

  1. Register the old key as a previous key so decryption can fall back: set app.previous_keys in config/app.php (Laravel) so both keys are tried.
  2. If the key genuinely rotated and old data is unrecoverable, treat the payload as expired and re-issue (e.g. flush sessions, re-set encrypted cookies).
  3. Verify the payload was not truncated or altered in transport (cookie size limits, proxy rewrites).

Example fix

// before
// config/app.php: 'key' => env('APP_KEY'),

// after (keep the old key available for decryption)
// .env: APP_KEY=newkey
//       APP_KEY_OLD=oldkey
// config/app.php:
'key' => env('APP_KEY'),
'previous_keys' => [
    env('APP_KEY_OLD'),
],
Defensive patterns

Strategy: try-catch

Validate before calling

// register previous keys so MAC validation can fall back
// config/app.php
'key' => env('APP_KEY'),
'previous_keys' => array_filter([
    env('APP_KEY_OLD'),
]),

Try / catch

use Illuminate\Contracts\Encryption\DecryptException;

try {
    return Crypt::decrypt($payload);
} catch (DecryptException $e) {
    if (str_contains($e->getMessage(), 'MAC is invalid')) {
        // key rotation scenario — invalidate the session/data and re-issue
        return null;
    }
    throw $e;
}

Prevention

When it happens

Trigger: Calling Crypt::decrypt() on a ciphertext that was encrypted with a key no longer in APP_KEY/previous_keys; on a payload whose value/iv/tag was altered (tampering); after an APP_KEY rotation without listing the old key in previous_keys.

Common situations: Rotating APP_KEY and forgetting to register the old key via Encrypter::previousKeys() / the app.previous_keys config; copying encrypted cookies/sessions between environments with different keys; a user manually editing an encrypted cookie; cross-environment data migration.

Related errors


AI-assisted analysis of laravel/framework@e0f6eb3518 (2026-08-11). Data as JSON: /api/errors/77cb7ea12447b492. Report an issue: GitHub.

Appendix: source

Thrown at src/Illuminate/Encryption/Encrypter.php:191

                if ($validMac && $validKey === null) {
                    $validKey = $key;
                }

                continue;
            }

            $decrypted = \openssl_decrypt(
                $payload['value'], strtolower($this->cipher), $key, 0, $iv, $tag ?? ''
            );

            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.
     *

View on GitHub (pinned to e0f6eb3518)