laravel/framework · error · InvalidArgumentException

The provided class must extend [{Collection::class}].

Error message

The provided class must extend [{Collection::class}].

What it means

Identical guard to AsCollection but inside AsEncryptedCollection, which decrypts the column (via Crypt::decryptString) before building the collection. The configured class must still extend Illuminate\Support\Collection. Thrown from the cast's get() when the encrypted attribute is read.

Source

Thrown at src/Illuminate/Database/Eloquent/Casts/AsEncryptedCollection.php:36

     * @return \Illuminate\Contracts\Database\Eloquent\CastsAttributes<\Illuminate\Support\Collection<array-key, mixed>, iterable>
     *
     * @throws \InvalidArgumentException
     */
    public static function castUsing(array $arguments)
    {
        return new class($arguments) implements CastsAttributes
        {
            public function __construct(protected array $arguments)
            {
                $this->arguments = array_pad(array_values($this->arguments), 2, '');
            }

            public function get($model, $key, $value, $attributes)
            {
                $collectionClass = empty($this->arguments[0]) ? Collection::class : $this->arguments[0];

                if (! is_a($collectionClass, Collection::class, true)) {
                    throw new InvalidArgumentException('The provided class must extend ['.Collection::class.'].');
                }

                if (! isset($attributes[$key])) {
                    return null;
                }

                $instance = new $collectionClass(Json::decode(Crypt::decryptString($attributes[$key])));

                if (! isset($this->arguments[1]) || ! $this->arguments[1]) {
                    return $instance;
                }

                if (is_string($this->arguments[1])) {
                    $this->arguments[1] = Str::parseCallback($this->arguments[1]);
                }

                return is_callable($this->arguments[1])
                    ? $instance->map($this->arguments[1])

View on GitHub (pinned to bd6b5437e6)

Solutions

  1. Ensure the configured class extends Illuminate\Support\Collection.
  2. Drop the custom class argument and use AsEncryptedCollection::class or AsEncryptedCollection::of(Item::class).
  3. Add a boot-time assertion: abort_unless(is_a($cls, \Illuminate\Support\Collection::class, true), 500).

Example fix

// before
protected $casts = [
    'notes' => AsEncryptedCollection::class.':'.NoteList::class,
];

// after
class NoteList extends \Illuminate\Support\Collection {}
// or simply:
protected $casts = ['notes' => AsEncryptedCollection::class];
Defensive patterns

Strategy: validation

Validate before calling

use Illuminate\Support\Collection;
$cls = \App\Support\SecureNotes::class;
if (! is_a($cls, Collection::class, true)) {
    throw new \LogicException("{$cls} must extend ".Collection::class);
}

Type guard

function isEncryptedCollectionSafe(string $class): bool
{
    return is_a($class, \Illuminate\Support\Collection::class, true);
}

Try / catch

try {
    return $model->notes;
} catch (\InvalidArgumentException $e) {
    if (str_contains($e->getMessage(), 'must extend [')) {
        report("AsEncryptedCollection cast misconfigured for " . get_class($model));
    }
    throw $e;
}

Prevention

When it happens

Trigger: Using 'secrets' => AsEncryptedCollection::using(SomeClass::class) where SomeClass is not a Collection subclass. Triggered on the first hydration/access of the encrypted attribute after the value is decrypted.

Common situations: Reusing a non-Collection DTO class intended for encrypted payloads; migrating from AsCollection to AsEncryptedCollection without re-checking that the collection class still extends the base; typos in the class string that resolve to an unrelated class.

Related errors


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