odoo/odoo · error · UserError

You cannot delete this account tag (%s), it is used on the c

Error message

You cannot delete this account tag (%s), it is used on the chart of account definition.

What it means

Raised by the @api.ondelete guard _unlink_except_master_tags on account.account.tag. Three XML-ID master tags (account_tag_operating, account_tag_financing, account_tag_investing) drive the cash-flow statement columns in the chart-of-accounts/report definitions; deleting them would break the standard reports, so Odoo refuses.

Source

Thrown at addons/account/models/account_account_tag.py:120

        return self.env['account.report.expression'].search(Domain('engine', '=', 'tax_tags') & Domain.OR(
            (
                Domain('report_line_id.report_id.country_id', '=', record.country_id.id)
                & Domain('formula', 'in', (record.name, '-' + record.name))
            )
            for record in self
        ))

    @api.ondelete(at_uninstall=False)
    def _unlink_except_master_tags(self):
        master_xmlids = [
            "account_tag_operating",
            "account_tag_financing",
            "account_tag_investing",
        ]
        for master_xmlid in master_xmlids:
            master_tag = self.env.ref(f"account.{master_xmlid}", raise_if_not_found=False)
            if master_tag and master_tag in self:
                raise UserError(_("You cannot delete this account tag (%s), it is used on the chart of account definition.", master_tag.name))

    def _translate_tax_tags(self, langs=None, tag_ids=None):
        """Translate tax tags having the same name as report lines."""
        langs = langs or (code for code, _name in self.env['res.lang'].get_installed() if code != 'en_US')
        for lang in langs:
            self.env.cr.execute(SQL(
                """
                UPDATE account_account_tag tag
                   SET name = tag.name || jsonb_build_object(%(lang)s, substring(tag.name->>'en_US' FOR 1) || (report_line.name->>%(lang)s))
                  FROM account_report_line report_line
                  JOIN account_report report ON report.id = report_line.report_id
                 WHERE tag.applicability = 'taxes'
                   AND tag.country_id = report.country_id
                   AND tag.name->>'en_US' = substring(tag.name->>'en_US' FOR 1) || (report_line.name->>'en_US')
                   AND tag.name->>%(lang)s != substring(tag.name->>'en_US' FOR 1) || (report_line.name->>%(lang)s)
                   %(and_tag_ids)s
                """,
                lang=lang,

View on GitHub (pinned to 1e661df964)

Solutions

  1. Exclude the three master tags from deletion: filter with lambda t: t not in (env.ref('account.account_tag_operating'), env.ref('account.account_tag_financing'), env.ref('account.account_tag_investing')).
  2. If they must be replaced, override/extend the report configuration instead of deleting the tags.
  3. In tests, use a guarded unlink or roll back the transaction rather than deleting master data.

Example fix

# before
env['account.account.tag'].search([...]).unlink()  # hits master tag

# after
masters = [env.ref('account.' + x) for x in
    ('account_tag_operating', 'account_tag_financing', 'account_tag_investing')]
tags = env['account.account.tag'].search([...]).filtered(lambda t: t not in masters)
tags.unlink()
Defensive patterns

Strategy: validation

Validate before calling

MASTER_TAG_XMLIDS = ('account.account_tag_operating',
                        'account.account_tag_financing',
                        'account.account_tag_investing')
master_ids = [env.ref(x, raise_if_not_found=False) for x in MASTER_TAG_XMLIDS]
master_ids = [t.id for t in master_ids if t]
deletable = tags.filtered(lambda t: t.id not in master_ids)

Try / catch

from odoo.exceptions import UserError
try:
    tags.unlink()
except UserError as e:
    if 'chart of account definition' in str(e):
        (tags - masters).unlink()
    else:
        raise

Prevention

When it happens

Trigger: Calling unlink() on an account.account.tag recordset that includes one of the three master tags resolved via env.ref('account.account_tag_operating'|'account_tag_financing'|'account_tag_investing').

Common situations: Bulk tag cleanup scripts that unlink all tags matching a pattern; attempting to 'reset' tags after a localization change by deleting everything; tests that wipe account.account.tag without filtering.

Related errors


AI-assisted analysis of odoo/odoo@1e661df964 (2026-08-15). Data as JSON: /api/errors/c199dc5f47378d53. Report an issue: GitHub.