yiisoft/yii2 · error · yii\base\InvalidArgumentException
Relation names are case sensitive. {class} has a relation na
Error message
Relation names are case sensitive. {class} has a relation named "{realName}" instead of "{name}". What it means
Lazy relation access goes through ActiveRelationTrait::findFor($name, $model): it looks up the getter method with method_exists() (which is case-insensitive in PHP), then compares lcfirst of the real method name after 'get' against the property name actually used. Because the comparison is strict, $model->Items still finds getItems() but then throws InvalidArgumentException explaining relation names are case sensitive.
Source
Thrown at framework/db/ActiveRelationTrait.php:184
$this->inverseOf = $relationName;
return $this;
}
/**
* Finds the related records for the specified primary record.
* This method is invoked when a relation of an ActiveRecord is being accessed lazily.
* @param string $name the relation name
* @param ActiveRecordInterface|BaseActiveRecord $model the primary model
* @return mixed the related record(s)
* @throws InvalidArgumentException if the relation is invalid
*/
public function findFor($name, $model)
{
if (method_exists($model, 'get' . $name)) {
$method = new \ReflectionMethod($model, 'get' . $name);
$realName = lcfirst(substr($method->getName(), 3));
if ($realName !== $name) {
throw new InvalidArgumentException('Relation names are case sensitive. ' . get_class($model) . " has a relation named \"$realName\" instead of \"$name\".");
}
}
return $this->multiple ? $this->all() : $this->one();
}
/**
* If applicable, populate the query's primary model into the related records' inverse relationship.
* @param array $result the array of related records as generated by [[populate()]]
* @since 2.0.9
*/
private function addInverseRelations(&$result)
{
if ($this->inverseOf === null) {
return;
}
foreach ($result as $i => $relatedModel) {View on GitHub (pinned to 66f00d18a2)
Solutions
- Use the exact case matching the getter minus 'get': getItems() is accessed as ->items
- Rename the getter to the casing you want and update all references
- For dynamic access, normalize the name first: $model->{lcfirst($name)}
- Grep for the wrongly-cased property in templates and API mappers after renaming relations
Example fix
// before $items = $order->Items; // getter is getItems() -> throws // after $items = $order->items;
Defensive patterns
Strategy: type-guard
Type guard
function hasExactCaseRelation(\yii\db\BaseActiveRecord $model, string $name): bool
{
if (!method_exists($model, 'get' . $name)) {
return false;
}
$method = new ReflectionMethod($model, 'get' . $name);
return lcfirst(substr($method->getName(), 3)) === $name;
} Try / catch
try {
$items = $order->items;
} catch (yii\base\InvalidArgumentException $e) {
if (strpos($e->getMessage(), 'Relation names are case sensitive') === 0) {
$items = $order->{lcfirst($relationName)}; // corrected casing
} else {
throw $e;
}
} Prevention
- Normalize dynamically built property names with lcfirst() before relation access
- When refactoring getters, grep templates and API mappers for all casings
- Add relation-name smoke tests that access every relation once
When it happens
Trigger: Accessing $order->Items when the getter is getItems(); $model->Author vs getAuthor(); dynamic access $model->{$name} where $name's casing came from user input or generated code.
Common situations: IDE autocompletion guessing wrong casing; refactors that rename getters without grepping all usages; template or API layer building property names dynamically; copy-pasting property access between codebases with different conventions.
Related errors
AI-assisted analysis of yiisoft/yii2@66f00d18a2 (2026-08-17).
Data as JSON: /api/errors/00f823ec1fe54b1c.
Report an issue: GitHub.