Skip to content

OD301 unresolved-xmlid

The XML id is not defined by its module.

Category Severity Series Needs the index
Correctness Error all yes

What it reports

A reference to an XML id that its module, which the referencing module does depend on, never defines. A module that Odoo always installs with base, such as web, counts as in depends. In data files the references checked are ref attributes, ref() in eval, inherit_id, a menu's parent and action, groups, and %(id)d in view attributes. In Python they are env.ref() and the groups given to has_group() or a field's groups. An id without a module prefix names one of the module's own.

Why it matters

Odoo resolves each reference in a data file while it loads the file, and an id that is not there stops the install with "External ID not found in the system". A demo file differs: see load failures. In Python, env.ref() raises ValueError when the line runs, which can be long after the install, on the one path a test did not cover. The usual cause is a typo, or an id that the other module renamed or removed in a new series.

The rule leaves alone what Odoo itself tolerates: ref('x', False) and env.ref('x', raise_if_not_found=False), an empty ref="" or inherit_id="", and an id built at runtime. It counts as defined the model_* and field_* ids Odoo creates for every model and field, the ids of the _inherits parents of records Odoo loads, ids that _update_xmlids assigns, and a record's id in any file of the module, since Python can load a file the manifest does not list. It skips the refs of data files the manifest does not load, upgrade scripts under migrations/ and upgrades/, ids of a module that is in depends but not indexed, such as an Enterprise one, ids of a module with an unread data file, which may define them, and in Python every id of a module that is not indexed.

From saas~14.3, where a data file names web.assets_backend, web.assets_common or web.assets_frontend, OD328 reports the element instead, since those bundles are no longer templates.

A field's id depends on the series. From saas~11.2 it is field_<model>__<field>, with the model's dots made underscores (saas-11.2 odoo/addons/base/models/ir_model.py:778). Below that it is field_<table>_<field>, with the model's _table (saas-11.1 odoo/addons/base/models/ir_model.py:760), so res.partner.name is field_res_partner_name on 10.0 and field_res_partner__name on 12.0. A database runs one series, so the module that writes the reference picks the spelling, and the other one reports, whatever the series of the field's module. A module with no series, in the manifest version or from --odoo-version, follows the field's module instead, and when that has none either, both spellings count. A Python file that does not parse hides its fields, so an id of its module spelled for one of its models counts as defined.

A spelling by table needs the table, which the scan may lack: a dependency is not indexed, and no indexed module introduces the model or sets its _table. An id of a module that extends such a model then counts as defined if it ends in a field the module declares on the model, after a table name no indexed model has. Any field_ id of that module counts when the class sits in a file that does not parse, has a Python base the scan cannot read, or takes an _inherits or _inherit parent no module indexes, as its fields may be among those the scan misses. An id for another field still reports. To have the table checked too, index the module that defines the model.

A model can have an id for a field it inherits, in the same spelling, and the module that holds it depends on the series. A module that declares the field and takes the parent itself holds the id, and so does the module that introduces a model for a field delegated through _inherits, whether the model or an _inherit parent sets it. On saas~11.5 a module that delegates a field it declares through a parent it adds to a model it extends holds nothing, which the rule does not tell apart.

For another field from a parent, whose declaring module loads before the model:

  • On 9.0, saas~11.3 and 11.4: the module that introduces the model, or adds the parent to a model it extends.
  • From 10.0 to saas~11.2: the module that declares the field on the parent. For a field the parent delegates, the module that adds the parent.
  • From saas~11.5 to 13.3: the module that introduces the model.
  • From saas~13.4: the module that introduces the model, or adds the parent to a model it extends.

When such a reference reports, name the module that holds the id in your series. From saas~11.5 to saas~13.3, no module holds the id of a field that an extension gets from a parent another module declares, so look the field up at runtime by model and name. A parent the scan cannot read hides its fields too, so an id of the module that inherits it, spelled for that model, counts as defined.

Example

views/sale_order_views.xml
<record id="view_order_form_customer_ref" model="ir.ui.view">
    <field name="name">sale.order.form.customer.ref</field>
    <field name="model">sale.order</field>
    <field name="inherit_id" ref="sale.view_order_from"/>
    <field name="arch" type="xml">
        <field name="payment_term_id" position="after">
            <field name="client_order_ref"/>
        </field>
    </field>
</record>

sale defines view_order_form, not view_order_from, so the install stops at this record.

views/sale_order_views.xml
<record id="view_order_form_customer_ref" model="ir.ui.view">
    <field name="name">sale.order.form.customer.ref</field>
    <field name="model">sale.order</field>
    <field name="inherit_id" ref="sale.view_order_form"/>
    <field name="arch" type="xml">
        <field name="payment_term_id" position="after">
            <field name="client_order_ref"/>
        </field>
    </field>
</record>

Suppressing

<!-- odooscan: ignore[OD301] -->

See suppressing a finding.