odoo/odoo · error · ValidationError
Line "%s" defines itself as its parent.
Error message
Line "%s" defines itself as its parent.
What it means
Raised by `_check_parent_line` on `account.report.line`: a line whose `parent_id` equals itself is rejected (`x.parent_id == x`). Self-parenting would create a cycle in the line tree, which the report renderer cannot traverse.
Source
Thrown at addons/account/models/account_report.py:447
try:
report_line._validate_groupby()
except UserError:
report_line.user_groupby = report_line.groupby
@api.constrains('parent_id')
def _validate_groupby_no_child(self):
for report_line in self:
if report_line.parent_id.groupby or report_line.parent_id.user_groupby:
raise ValidationError(_("A line cannot have both children and a groupby value (line '%s').", report_line.parent_id.name))
@api.constrains('groupby', 'user_groupby')
def _validate_groupby(self):
self.expression_ids._validate_engine()
@api.constrains('parent_id')
def _check_parent_line(self):
for line in self.filtered(lambda x: x.parent_id == x):
raise ValidationError(_('Line "%s" defines itself as its parent.', line.name))
def _copy_hierarchy(self, copied_report, parent=None, code_mapping=None):
''' Copy the whole hierarchy from this line by copying each line children recursively and adapting the
formulas with the new copied codes.
:param copied_report: The copy of the report.
:param parent: The parent line in the hierarchy (a copy of the original parent line).
:param code_mapping: A dictionary keeping track of mapping old_code -> new_code
'''
self.ensure_one()
copied_line = self.copy({
'report_id': copied_report.id,
'parent_id': parent and parent.id,
'code': self._get_copied_code(),
})
# Keep track of old_code -> new_code in a mutable dictView on GitHub (pinned to 1e661df964)
Solutions
- Set `parent_id` to a *different* line, or to `False` for a root line
- In copy code, skip the self-reference: map `parent_id` only when `new_parent != copied_line`
- Validate imports up front: reject rows whose parent code equals their own code
Example fix
# before
new_line = line.copy({'parent_id': new_line_id_map[line.id]}) # map may contain line.id -> its own new id
# after
new_line = line.copy({'parent_id': new_line_id_map.get(line.parent_id.id, False)}) Defensive patterns
Strategy: validation
Validate before calling
if vals.get('parent_id') and vals['parent_id'] == line.id:
vals['parent_id'] = False # or raise
line.write(vals) Try / catch
from odoo.exceptions import ValidationError
try:
line.write({'parent_id': parent_id})
except ValidationError:
line.write({'parent_id': False}) Prevention
- In copy/remap code, build the old->new parent map from `line.parent_id`, never from `line.id`
- Validate imported rows: parent code must differ from the line's own code
- Unit-test hierarchy builders for cycles
When it happens
Trigger: `create()`/`write()` with `parent_id` set to the same record as the line itself — e.g. after copying a line and remapping parent ids with a buggy mapping dict that maps the line to itself, or assigning `parent_id` from a variable that accidentally holds the line.
Common situations: Copy/duplicate routines that remap `parent_id` via an old-id-to-new-id dict and hit a self-reference when the mapping is built incorrectly; import scripts that resolve parent codes to the same line; drag-and-drop re-parenting bugs in custom UIs.
Related errors
- Line "%(line)s" defines line "%(parent_line)s" as its parent
- A line cannot have both children and a groupby value (line '
- Only a report without a root report of its own can be select
- The sections defined on a report cannot have sections themse
- The Availability is set to 'Country Matches' but the field C
AI-assisted analysis of odoo/odoo@1e661df964 (2026-08-15).
Data as JSON: /api/errors/bbb9336887c3bbac.
Report an issue: GitHub.