laravel/framework · error · InvalidArgumentException
The cast object for the
Error message
The cast object for the {$attribute} attribute must implement Stringable. What it means
Thrown by ensureCastsAreStringValues() when a cast entry is an object that does not implement Stringable. When casts are merged/resolved, Laravel normalizes each cast to a string form (e.g. 'ClassName:arg1,arg2'); an object that cannot be cast to string via __toString breaks that normalization, so it is rejected with an explicit error.
Solutions
- Implement \Stringable on the caster class and add __toString() returning the cast string (e.g. the class name).
- Use the class name string (e.g. SomeCaster::class) in $casts instead of an instance.
- If using a class-based cast, register it via the Castable infrastructure or as 'classname:args'.
Example fix
// before
protected $casts = [
'settings' => new App\Casts\SettingsCast, // not Stringable
];
// after
class SettingsCast implements \Stringable
{
public function __toString(): string
{
return static::class;
}
}
protected $casts = [
'settings' => new App\Casts\SettingsCast,
]; Defensive patterns
Strategy: validation
Validate before calling
foreach ((function () { $m = new YourModel; $r = new \ReflectionObject($m); return $r->getProperty('casts')->getValue($m); })() as $attr => $cast) {
if (is_object($cast) && ! ($cast instanceof \Stringable)) {
throw new \LogicException("Cast for {$attr} must implement Stringable");
}
} Type guard
function castObjectIsStringable(mixed $cast): bool
{
return is_object($cast) && $cast instanceof \Stringable;
} Prevention
- Prefer class-name strings in $casts over object instances.
- If using a caster object, implement \Stringable with __toString().
- Add a boot-time test that iterates $casts and asserts each object entry is Stringable.
When it happens
Trigger: Assigning a non-Stringable object instance to $casts (e.g. protected $casts = ['data' => new SomeCaster];) where SomeCaster does not implement \Stringable. Triggered when casts are processed/merged (e.g. via mergeCasts / hasCasts / getCasts).
Common situations: Passing a custom caster class instance (instead of its class name) that was not marked Stringable; third-party cast package whose object form predates the Stringable requirement; refactoring casts from strings to object instances during an upgrade.
Related errors
- Call to undefined cast
- Could not verify the hashed value's configuration.
- The provided class must extend…
- The provided class must extend…
- Expected class to implement guessResourceName method. Make…
AI-assisted analysis of laravel/framework@e0f6eb3518 (2026-08-11).
Data as JSON: /api/errors/a892d5115c296943.
Report an issue: GitHub.
Appendix: source
Thrown at src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php:821
/**
* Ensure that the given casts are strings.
*
* @param array $casts
* @return array
*
* @throws \InvalidArgumentException
*/
protected function ensureCastsAreStringValues($casts)
{
foreach ($casts as $attribute => $cast) {
$casts[$attribute] = match (true) {
is_object($cast) => value(function () use ($cast, $attribute) {
if ($cast instanceof Stringable) {
return (string) $cast;
}
throw new InvalidArgumentException(
"The cast object for the {$attribute} attribute must implement Stringable."
);
}),
is_array($cast) => value(function () use ($cast) {
if (count($cast) === 1) {
return $cast[0];
}
[$cast, $arguments] = [array_shift($cast), $cast];
return $cast.':'.implode(',', $arguments);
}),
default => $cast,
};
}
return $casts;
}View on GitHub (pinned to e0f6eb3518)