octobercms/october · error · SystemException
Missing relation definition for inverse field '{$this->inver
Error message
Missing relation definition for inverse field '{$this->inverse}' for model class '{$this->modelClass}' for '{$this->fieldName}'. What it means
For a recordfinder field with `inverse:`, Tailor requires the related model to already define a relation with that name ($relatedModel->hasRelation($this->inverse)) before wiring the inverse. If the target blueprint never created such a relation — typically because the inverse side is not an entries field pointing back — the SystemException fires during model extension.
Source
Thrown at modules/tailor/contentfields/RecordFinderField.php:222
'table' => $model->getBlueprintDefinition()->getJoinTableName(),
'name' => $this->fieldName,
'relationClass' => CustomMultiJoinRelation::class,
'relatedKey' => ($relatedMultisite && !$isDualMultisite) ? 'site_root_id' : 'id',
'relatedMultisite' => $relatedMultisite
];
}
}
/**
* defineInverseModelRelationship for the inverse relationship. These definitions
* do not rely on multisite via propagatable attribute. Inverse relations do not
* replicate and should be treated as read-only.
*/
protected function defineInverseModelRelationship($model)
{
$relatedModel = $this->getRelatedModel();
if (!$relatedModel->hasRelation($this->inverse)) {
throw new SystemException("Missing relation definition for inverse field '{$this->inverse}' for model class '{$this->modelClass}' for '{$this->fieldName}'.");
}
$isNested = $model instanceof RepeaterItem;
// RepeaterItem doesn't have getBlueprintDefinition
$parentMultisite = $isNested ? false : $model->getBlueprintDefinition()->useMultisite();
$relationDefinition = $relatedModel->getRelationDefinition($this->inverse);
$relatedMultisite = $relatedModel->isClassInstanceOf(\October\Contracts\Database\MultisiteInterface::class) &&
$relatedModel->isMultisiteEnabled();
// Dual-multisite: both parent and related use multisite
$isDualMultisite = $parentMultisite && $relatedMultisite;
$isSingular = $this->maxItems === 1;
$otherIsSingular = $relatedModel->isRelationTypeSingular($this->inverse);
$otherIsPropagatable = $relatedMultisite ? $relatedModel->isAttributePropagatable($this->inverse) : false;
// In dual-multisite, use 'id' for proper site isolation; otherwise use site_root_id for sharingView on GitHub (pinned to b608633a7e)
Solutions
- Define an entries field named exactly like the `inverse:` value on the source blueprint so the relation exists on its model.
- Remove `inverse:` if bidirectional navigation isn't needed.
- Re-run `php artisan tailor:refresh` after the change so model relations are rebuilt.
Example fix
# before (recordfinder)
fields:
primary_author:
type: recordfinder
source: authors
inverse: authored_posts # no such relation on Author model
# after (authors blueprint must define)
fields:
authored_posts:
type: entries
source: blog/posts
inverse: primary_author Defensive patterns
Strategy: validation
Validate before calling
$relatedModel = $sourceBlueprint->newModelInstance();
if (!$relatedModel->hasRelation($inverse)) {
throw new InvalidArgumentException("Related model has no relation '{$inverse}' — define an entries field with that handle on the source blueprint.");
} Type guard
function inverseRelationExists($relatedModel, string $inverse): bool
{
return $relatedModel->hasRelation($inverse);
} Try / catch
try {
// model extension / tailor:refresh
} catch (\SystemException $e) {
// message names the missing relation; add the matching entries field on the source blueprint and refresh
} Prevention
- Before adding `inverse:` to a recordfinder, confirm the target model exposes that relation (an entries field with the same handle).
- Keep both sides of an inverse pair in one commit and refresh Tailor after changes.
- Never rename one side of an inverse pair without grepping for the old handle.
When it happens
Trigger: `inverse: items` is set on a recordfinder whose source blueprint/model defines no `items` relation: the inverse field was renamed, is a different field type that doesn't generate that relation, or the source blueprint failed to compile its relations.
Common situations: Assuming `inverse:` creates the reverse relation instead of requiring one to exist; renaming the entries field on the source blueprint without updating the recordfinder; deploying only one side of a blueprint pair.
Related errors
- Invalid inverse field '{$this->inverse}' for source '{$this-
- The provided model class ":modelClass" for the recordfinder
- Type must be one of: {$typeAsString}.
- Field name is reserved: {$fieldName}.
- Invalid source reference '{$source}'. No blueprint found wit
AI-assisted analysis of octobercms/october@b608633a7e (2026-08-21).
Data as JSON: /api/errors/0796dabda559a22b.
Report an issue: GitHub.