{"record":{"id":"c610370c2bbe736a","repo":"yiisoft/yii2","slug":"value-must-be-convertable-to-string","errorCode":null,"errorMessage":"Value must be convertable to string.","messagePattern":"Value must be convertable to string\\.","errorType":"exception","errorClass":"yii\\base\\InvalidConfigException","httpStatus":null,"severity":"error","filePath":"framework/db/ActiveRelationTrait.php","lineNumber":613,"sourceCode":"            }\n        }\n        if (count($key) > 1) {\n            return serialize($key);\n        }\n        return reset($key);\n    }\n\n    /**\n     * @param mixed $value raw key value. Since 2.0.40 non-string values must be convertible to string (like special\n     * objects for cross-DBMS relations, for example: `|MongoId`).\n     * @return string normalized key value.\n     */\n    private function normalizeModelKey($value)\n    {\n        try {\n            return (string)$value;\n        } catch (\\Exception $e) {\n            throw new InvalidConfigException('Value must be convertable to string.');\n        } catch (\\Throwable $e) {\n            throw new InvalidConfigException('Value must be convertable to string.');\n        }\n    }\n\n    /**\n     * @param array $primaryModels either array of AR instances or arrays\n     * @return array\n     */\n    private function findJunctionRows($primaryModels)\n    {\n        if (empty($primaryModels)) {\n            return [];\n        }\n        $this->filterByModels($primaryModels);\n        /** @var ActiveRecord $primaryModel */\n        $primaryModel = reset($primaryModels);\n        if (!$primaryModel instanceof ActiveRecordInterface) {","sourceCodeStart":595,"sourceCodeEnd":631,"githubUrl":"https://github.com/yiisoft/yii2/blob/66f00d18a29b520f85e8e8f1e32d1e7e7b556cac/framework/db/ActiveRelationTrait.php#L595-L631","documentation":"When a relation is populated, ActiveRelationTrait::filterByModels() hashes the primary models' key values through normalizeModelKey(), which casts each value to string. Since 2.0.40 non-string values must be convertible; casting an array or an object without __toString() raises an engine error that the catch (\\Exception) branch converts into InvalidConfigException with the message 'Value must be convertable to string.'","triggerScenarios":"A relation's link points at an attribute that currently holds an array (e.g. a JSON column decoded to an array) or an object lacking __toString(); using relation methods with model instances whose key attributes were filled from unvalidated form input.","commonSituations":"JSON columns storing identifiers; upgrading from Yii < 2.0.40 where the cast silently produced garbage instead of an exception; custom key objects (cross-DBMS, e.g. MongoId-like classes) that never implemented __toString().","solutions":["Ensure the linked attribute holds a scalar (string/int) before accessing or eagerly loading the relation","Implement __toString() on custom key-object classes used in link columns","Fix the relation's link definition so it targets a real scalar column, not a container attribute","For lists of ids stored in one attribute, query manually with where(['id' => $array]) instead of a relation"],"exampleFix":"// before\npublic function getOrders()\n{\n    return $this->hasMany(Order::class, ['customer_id' => 'id']);\n}\n// $customer->id is an array (bad data) -> throws on $customer->orders\n\n// after\n$customer->id = (int) $raw; // normalize before relation access\n$orders = $customer->orders;","handlingStrategy":"validation","validationCode":"foreach ($models as $model) {\n    $keyValue = $model->{$pkColumn};\n    if (!is_scalar($keyValue) && !(is_object($keyValue) && method_exists($keyValue, '__toString'))) {\n        throw new InvalidArgumentException('Relation key must be scalar or string-convertible.');\n    }\n}","typeGuard":"function isUsableRelationKey($value): bool\n{\n    return is_scalar($value) || (is_object($value) && method_exists($value, '__toString'));\n}","tryCatchPattern":"try {\n    $orders = $customer->orders;\n} catch (yii\\base\\InvalidConfigException $e) {\n    if (strpos($e->getMessage(), 'convertable to string') !== false) {\n        // key attribute holds a non-scalar: fail loudly with the offending value\n        throw new RuntimeException('Non-scalar relation key on customer.id', 0, $e);\n    }\n    throw $e;\n}","preventionTips":["Validate relation key attributes at hydration time, not query time","Implement __toString() on custom key objects","Add tests exercising relations with real production-shaped key data"],"tags":["relations","type-conversion","primary-key","string-cast"],"backgroundTag":"object-not-string-convertible","analyzedSha":"66f00d18a29b520f85e8e8f1e32d1e7e7b556cac","analyzedAt":"2026-08-17T05:17:23.470Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}