{"record":{"id":"8e2778c220305834","repo":"phalcon/cphalcon","slug":"row-is-an-immutable-arrayaccess-object","errorCode":null,"errorMessage":"Row is an immutable ArrayAccess object","messagePattern":"Row is an immutable ArrayAccess object","errorType":"exception","errorClass":"Phalcon\\Mvc\\Model\\Exceptions\\RowIsImmutable","httpStatus":null,"severity":"error","filePath":"phalcon/Mvc/Model/Row.zep","lineNumber":70,"sourceCode":"     */\n    public function offsetGet(mixed index) -> mixed\n    {\n        if !property_exists(this, index) {\n            throw new IndexNotInRow();\n        }\n\n        return this->{index};\n    }\n\n    /**\n     * Rows cannot be changed. It has only been implemented to meet the definition of the ArrayAccess interface\n     *\n     * @param string|int offsetSet\n     * @param ModelInterface value\n     */\n    public function offsetSet(mixed offset, mixed value) -> void\n    {\n        throw new RowIsImmutable();\n    }\n\n    /**\n     * Rows cannot be changed. It has only been implemented to meet the definition of the ArrayAccess interface\n     *\n     * @param string|int offset\n     */\n    public function offsetUnset(mixed offset) -> void\n    {\n        throw new RowIsImmutable();\n    }\n\n    /**\n     * Reads an attribute value by its name\n     *\n     *```php\n     * echo $invoice->readAttribute(\"inv_title\");\n     *```","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/phalcon/cphalcon/blob/b7419de9cd0a8a3f48441ead84c9f8415d463e25/phalcon/Mvc/Model/Row.zep#L52-L88","documentation":"Row objects returned by Phalcon ORM queries for partial selects are immutable snapshots. offsetSet() exists only to satisfy the ArrayAccess interface and unconditionally throws RowIsImmutable, because mutating a hydrated result would desynchronize it from the data source and break identity tracking.","triggerScenarios":"Executing $row['key'] = $value on any Phalcon\\Mvc\\Model\\Row instance; trying to attach computed fields to a result row before passing it to a view or serializer; attempting to reuse a fetched Row as a template by overwriting its fields.","commonSituations":"Controllers that fetch partial rows and then decorate them with extra keys for the view; API layers that want to append metadata to results; developers used to hydrating to plain arrays assuming Rows behave the same.","solutions":["Convert the Row to a plain array first, then mutate the array: $data = (array) $row (or get_object_vars($row) / iterator_to_array($row)), then $data['extra'] = ...","Compute derived values directly in the query using SQL expressions and aliases so no mutation is needed","If you always want arrays, use a result set hydration strategy that returns arrays instead of Row objects","Keep the Row read-only and carry computed values in separate variables passed alongside it"],"exampleFix":"// before\n$row = $query->getSingleResult();\n$row['discounted'] = true; // throws RowIsImmutable\n\n// after: copy to an array, then extend it\n$data = (array) $row;\n$data['discounted'] = true;\nreturn $data;","handlingStrategy":"fallback","validationCode":"// Before any mutation attempt: branch on the result type\nif ($result instanceof \\Phalcon\\Mvc\\Model\\Row) {\n    $data = (array) $result;      // mutable copy\n} else {\n    $data = (array) $result;\n}\n$data['extra'] = 'value';","typeGuard":"function isImmutableRow(mixed $value): bool\n{\n    return $value instanceof \\Phalcon\\Mvc\\Model\\Row;\n}","tryCatchPattern":"try {\n    $row['key'] = $value;\n} catch (\\Phalcon\\Mvc\\Model\\Exceptions\\RowIsImmutable $e) {\n    $data = (array) $row;\n    $data['key'] = $value; // mutate the copy instead\n}","preventionTips":["Treat every Phalcon ORM result as read-only at the boundary; copy to array before decorating","Push computed values into the query (SQL aliases) when possible","Encode the rule in code review: no assignment or unset on query results"],"tags":["phalcon","orm","immutability","arrayaccess","result-row"],"backgroundTag":"immutable-object-mutation","analyzedSha":"b7419de9cd0a8a3f48441ead84c9f8415d463e25","analyzedAt":"2026-08-21T06:21:18.811Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}