{"record":{"id":"441115bb3b2dc1f7","repo":"laravel/framework","slug":"model-attribute-value-is-an-object-but-does-not-ha","errorCode":null,"errorMessage":"Model attribute value is an object but does not have a __toString method.","messagePattern":"Model attribute value is an object but does not have a __toString method\\.","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithDictionary.php","lineNumber":35,"sourceCode":"     *\n     * @throws \\InvalidArgumentException\n     */\n    protected function getDictionaryKey($attribute)\n    {\n        if (is_null($attribute) || is_string($attribute) || is_int($attribute)) {\n            return $attribute;\n        }\n\n        if (is_object($attribute)) {\n            if (method_exists($attribute, '__toString')) {\n                return $attribute->__toString();\n            }\n\n            if ($attribute instanceof UnitEnum) {\n                return enum_value($attribute);\n            }\n\n            throw new InvalidArgumentException('Model attribute value is an object but does not have a __toString method.');\n        }\n\n        return (string) $attribute;\n    }\n}\n","sourceCodeStart":17,"sourceCodeEnd":41,"githubUrl":"https://github.com/laravel/framework/blob/e0f6eb3518ac29fbbca8529e97d0df7fc9f24481/src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithDictionary.php#L17-L41","documentation":"getDictionaryKey() converts relationship keys (foreign keys, owner keys, morph maps) into a usable scalar for the internal dictionary lookup. When it receives an object that is neither a string-castable value (__toString), nor a UnitEnum (handled by enum_value), it throws, because there is no safe way to use that object as a dictionary key.","triggerScenarios":"A model's primary/foreign key attribute resolving to a plain object (e.g. a value object without __toString) when a relationship tries to build its dictionary - common with custom cast classes that return objects, or with attach()/sync() receiving an object as a key.","commonSituations":"Custom key casts returning value objects (UUID/Money/Decimal objects) without __toString; attaching a model whose getKey() returns an object; enums used as keys on PHP < 8.1 paths; morph map values that are objects.","solutions":["Implement __toString() on the custom value object used as a key.","If the object is a BackedEnum, ensure enum_value() can resolve it (it does for UnitEnum).","Adjust the custom cast's get() to return a scalar string/int instead of an object for key columns.","Pass scalar IDs to attach()/sync() instead of object keys."],"exampleFix":"// before - value object as a key column\nclass Uuid { public function __construct(public string $value) {} }\nclass User extends Model {\n    protected $casts = ['id' => UuidCast::class]; // returns Uuid object\n}\n$user->roles()->attach($roleId); // throws\n\n// after\nclass Uuid {\n    public function __construct(public string $value) {}\n    public function __toString(): string { return $this->value; }\n}","handlingStrategy":"type-guard","validationCode":"// Ensure key values used by relationships are scalar/stringable\n$key = $model->getKey();\nif (is_object($key) && ! method_exists($key, '__toString') && ! ($key instanceof \\UnitEnum)) {\n    throw new \\RuntimeException('Model key is an object without __toString; relationship lookups will fail.');\n}","typeGuard":"function isUsableDictionaryKey(mixed $value): bool {\n    if ($value === null || is_string($value) || is_int($value)) return true;\n    if (is_object($value)) {\n        return method_exists($value, '__toString') || $value instanceof \\UnitEnum;\n    }\n    return true;\n}","tryCatchPattern":"try {\n    $model->roles()->attach($roleId);\n} catch (\\InvalidArgumentException $e) {\n    if (str_contains($e->getMessage(), 'does not have a __toString method')) {\n        $roleId = is_object($roleId) ? (string) $roleId : $roleId; // coerce after fixing the VO\n    } else { throw $e; }\n}","preventionTips":["Implement __toString on any custom value object used as a key/cast output.","Have key casts return scalars (string/int) rather than objects.","Pass scalar IDs to attach()/sync() rather than object keys.","For BackedEnum keys, ensure you are on PHP 8.1+ so UnitEnum handling applies."],"tags":["eloquent","relationships","dictionary-key","casts","value-object"],"backgroundTag":null,"analyzedSha":"e0f6eb3518ac29fbbca8529e97d0df7fc9f24481","analyzedAt":"2026-08-11T20:52:37.562Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}