phalcon/cphalcon · error · Phalcon\Mvc\Model\Exceptions\BelongsToRequiresObject
Only objects can be stored as part of belongs-to relations i
Error message
Only objects can be stored as part of belongs-to relations in '{className}' Relation {relationName} What it means
Thrown by Model::preSaveRelatedRecords() (phalcon/Mvc/Model.zep:5468): a belongs-to relation was assigned a non-object value. BelongsTo relations hold exactly one referenced model instance, and its primary key is copied into the local FK after the related record is saved, so only objects are accepted. The exception class is Phalcon\Mvc\Model\Exceptions\BelongsToRequiresObject; the implicit transaction is rolled back first.
Source
Thrown at phalcon/Mvc/Model.zep:5468
let relation = <RelationInterface> manager->getRelationByAlias(
className,
name
);
if typeof relation === "object" {
/**
* Get the relation type
*/
let type = relation->getType();
/**
* Only belongsTo are stored before save the master record
*/
if type == Relation::BELONGS_TO {
if unlikely typeof record !== "object" {
connection->rollback(nesting);
throw new BelongsToRequiresObject(get_class(this), name);
}
/**
* If dynamic update is enabled, saving the record must not take any action.
* Recursion through circular relations is prevented by the visited
* collection inside doSave().
*/
if !record->doSave(visited) {
/**
* Get the validation messages generated by the
* referenced model
*/
this->appendMessagesFrom(record);
/**
* Rollback the implicit transaction
*/
connection->rollback(nesting);View on GitHub (pinned to b7419de9cd)
Solutions
- Assign a model instance: $invoice->customer = Customer::findFirst(123);
- If you only have the key, set the FK column directly instead of the relation property
- For create-from-scratch flows, build the related model first: $invoice->customer = new Customer([...]);
Example fix
// before $invoice = new Invoice([...]); $invoice->customer = 123; // BelongsToRequiresObject $invoice->save(); // after $invoice->customer = Customer::findFirst(123); // or set the FK directly: $invoice->customerId = 123; $invoice->save();
Defensive patterns
Strategy: type-guard
Validate before calling
if (isset($invoice->customer) && !$invoice->customer instanceof \Phalcon\Mvc\ModelInterface) {
// belongsTo relation must hold a model instance before save()
$invoice->customer = Customer::findFirst((int) $invoice->customer);
} Type guard
function isModelInstance($value): bool
{
return $value instanceof \Phalcon\Mvc\ModelInterface;
}
// before save:
if (null !== $invoice->customer && !isModelInstance($invoice->customer)) {
throw new InvalidArgumentException('customer relation expects a Model instance');
} Try / catch
try {
$invoice->save();
} catch (\Phalcon\Mvc\Model\Exceptions\BelongsToRequiresObject $e) {
// relation property holds a scalar; set the FK column instead
$relationName = 'customer'; // parse from message tail
$invoice->{$relationName . 'Id'} = $scalarId;
$invoice->save();
} Prevention
- Convention: assign FK columns for keys, relation properties for instances; never mix
- Whitelist and cast request data before assigning to relation properties
- Unit-test each relation property with an instance to lock the contract
When it happens
Trigger: Assigning a scalar to a belongsTo relation property and saving: $invoice->customer = 123; $invoice->save(). Also assigning an array, a stdClass, or null where a Model instance is required.
Common situations: Confusing FK column assignment ($invoice->customerId = 123) with relation assignment ($invoice->customer = 123); API payloads hydrated directly into relation properties; refactoring from raw FK properties to relations without updating setters.
Related errors
- Only objects/arrays can be stored as part of has-many/has-on
- Class '{className}' is not an ADR Action.
- Service '{name}' not registered
- A dependency which implements CryptInterface is required to
- Identity column '{identityField}' isn't part of the column m
AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21).
Data as JSON: /api/errors/73f0b319f1d3606b.
Report an issue: GitHub.