laravel/framework · error · DecryptException

The payload is invalid.

Error message

The payload is invalid.

What it means

getJsonPayload() throws DecryptException('The payload is invalid.') at line 244 when the input is not a string — decrypt() was given null, an array, an int, etc. This is the first guard before any base64/json decoding; it catches callers passing the wrong type entirely (e.g. feeding already-decoded data back into decrypt).

Source

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

     * @return string
     */
    protected function hash(#[\SensitiveParameter] $iv, #[\SensitiveParameter] $value, #[\SensitiveParameter] $key)
    {
        return hash_hmac('sha256', $iv.$value, $key);
    }

    /**
     * Get the JSON array from the given payload.
     *
     * @param  string  $payload
     * @return array
     *
     * @throws \Illuminate\Contracts\Encryption\DecryptException
     */
    protected function getJsonPayload($payload)
    {
        if (! is_string($payload)) {
            throw new DecryptException('The payload is invalid.');
        }

        $payload = json_decode(base64_decode($payload), true);

        // If the payload is not valid JSON or does not have the proper keys set we will
        // assume it is invalid and bail out of the routine since we will not be able
        // to decrypt the given value. We'll also check the MAC for this encryption.
        if (! $this->validPayload($payload)) {
            throw new DecryptException('The payload is invalid.');
        }

        return $payload;
    }

    /**
     * Verify that the encryption payload is valid.
     *
     * @param  mixed  $payload

View on GitHub (pinned to bd6b5437e6)

Solutions

  1. Check the input is a non-empty string before calling decrypt: if (is_string($v) && $v !== '') { decrypt($v); }.
  2. Use Encrypter::appearsEncrypted($v) to confirm shape before attempting decryption.
  3. Treat null/missing inputs as 'no data' in your domain logic rather than piping them through decrypt().
  4. For optional cookies/headers, return a default and skip decryption when absent.

Example fix

// before
$value = decrypt($request->cookie('prefs')); // cookie may be null

// after
$raw = $request->cookie('prefs');
$value = is_string($raw) && Encrypter::appearsEncrypted($raw)
    ? decrypt($raw)
    : defaultPrefs();
Defensive patterns

Strategy: type-guard

Validate before calling

$raw = $request->cookie('prefs');
if (! is_string($raw) || $raw === '') {
    return defaultPrefs();
}
return decrypt($raw);

Type guard

function isDecryptableString(mixed $value): bool
{
    return is_string($value) && $value !== ''
        && \Illuminate\Encryption\Encrypter::appearsEncrypted($value);
}

Prevention

When it happens

Trigger: Calling decrypt(null), decrypt([]) or decrypt(123) — typically because the caller read a missing cookie/header (null) or pulled an already-parsed value from somewhere and passed it straight to decrypt(). Common in cookie/session middleware reading optional inputs.

Common situations: Cookies not present on the request ($request->cookie('foo') returns null); a header missing; reading encrypted values from JSON requests where the field is absent; double-decoding (passing the decoded array back into decrypt).

Related errors


AI-assisted analysis of laravel/framework@bd6b5437e6 (2026-08-06). Data as JSON: /data/errors/b1c2d17061b10bac.json. Report an issue: GitHub.