{"record":{"id":"f346e4282a153511","repo":"yiisoft/yii2","slug":"invalid-link-it-must-be-an-array-of-key-value-pai","errorCode":null,"errorMessage":"Invalid link: it must be an array of key-value pairs.","messagePattern":"Invalid link: it must be an array of key-value pairs\\.","errorType":"exception","errorClass":"yii\\base\\InvalidConfigException","httpStatus":null,"severity":"error","filePath":"framework/db/ActiveRelationTrait.php","lineNumber":229,"sourceCode":"                    $modelClass = $this->modelClass;\n                    $inverseRelation = $modelClass::instance()->getRelation($this->inverseOf);\n                }\n                $result[$i][$this->inverseOf] = $inverseRelation->multiple ? [$this->primaryModel] : $this->primaryModel;\n            }\n        }\n    }\n\n    /**\n     * Finds the related records and populates them into the primary models.\n     * @param string $name the relation name\n     * @param array $primaryModels primary models\n     * @return array the related models\n     * @throws InvalidConfigException if [[link]] is invalid\n     */\n    public function populateRelation($name, &$primaryModels)\n    {\n        if (!is_array($this->link)) {\n            throw new InvalidConfigException('Invalid link: it must be an array of key-value pairs.');\n        }\n\n        if ($this->via instanceof self) {\n            // via junction table\n            /** @var self<ActiveRecord|array<string, mixed>> $viaQuery */\n            $viaQuery = $this->via;\n            $viaModels = $viaQuery->findJunctionRows($primaryModels);\n            $this->filterByModels($viaModels);\n        } elseif (is_array($this->via)) {\n            // via relation\n            /** @var self<ActiveRecord|array<string, mixed>>|ActiveQueryTrait $viaQuery */\n            list($viaName, $viaQuery) = $this->via;\n            if ($viaQuery->asArray === null) {\n                // inherit asArray from primary query\n                $viaQuery->asArray($this->asArray);\n            }\n            $viaQuery->primaryModel = null;\n            $viaModels = array_filter($viaQuery->populateRelation($viaName, $primaryModels));","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/yiisoft/yii2/blob/66f00d18a29b520f85e8e8f1e32d1e7e7b556cac/framework/db/ActiveRelationTrait.php#L211-L247","documentation":"populateRelation() requires the relation query's link property to be an array of key-value pairs mapping primary-model columns to related-model columns. hasMany()/hasOne() normally set it, but when a relation getter passes a non-array $link, or a hand-built ActiveQuery never sets ->link, InvalidConfigException is thrown the moment the relation is populated, eagerly (joinWith/with) or lazily.","triggerScenarios":"Defining return $this->hasMany(Item::class, 'order_id') (string link) instead of ['order_id' => 'id']; building an ActiveQuery manually for a relation and forgetting ->link = [...]; calling joinWith('malformedRelation') where the getter returns such a query.","commonSituations":"Habits carried from other AR libraries where a single string names the foreign key; incomplete copy-paste of relation examples; refactoring relation getters and dropping the array brackets.","solutions":["Pass the link as an array: $this->hasMany(Item::class, ['order_id' => 'id']) where the key is the related table's column and the value the primary table's column","Audit every getXyz() relation getter in the involved model for a malformed link argument","For manually constructed relation queries, set ->link explicitly to an array before returning them"],"exampleFix":"// before\npublic function getItems()\n{\n    return $this->hasMany(Item::class, 'order_id'); // string link -> throws on populate\n}\n\n// after\npublic function getItems()\n{\n    return $this->hasMany(Item::class, ['order_id' => 'id']);\n}","handlingStrategy":"validation","validationCode":"// sanity-check a relation definition before heavy use\n$relation = $model->getItems();\nif (!is_array($relation->link) || $relation->link === []) {\n    throw new InvalidConfigException('Relation getItems() must define an array link.');\n}","typeGuard":null,"tryCatchPattern":"try {\n    $items = $order->items;\n} catch (yii\\base\\InvalidConfigException $e) {\n    if (strpos($e->getMessage(), 'Invalid link') === 0) {\n        throw new RuntimeException('Broken relation definition in ' . get_class($order) . '::getItems()', 0, $e);\n    }\n    throw $e;\n}","preventionTips":["Always write links as arrays: hasMany(X::class, ['fk' => 'pk'])","Add a unit test that calls every relation getter and asserts is_array($query->link)","Code-review relation getters for missing array brackets"],"tags":["relations","configuration","link","active-record"],"backgroundTag":"invalid-relation-config","analyzedSha":"66f00d18a29b520f85e8e8f1e32d1e7e7b556cac","analyzedAt":"2026-08-17T05:17:23.470Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}