Cet article est actuellement disponible en anglais uniquement. Traduction à venir.
You install or update a custom Odoo module and the log dies with:
ERROR: External ID not found in the system: sale_extension.sequence_priority_quote
Or:
ValueError: Data not found: sale_extension.action_window_priority
Neither variant aborts every install — sometimes the module finishes "successfully" with the data missing, which is worse. This error covers a family of XML data loading failures across Odoo 17.0/18.0/19.0.
Quick Fix
The error always names a fully-qualified XML ID like module_name.record_id. Find that record's definition file and verify three things in order:
- The file is listed in
__manifest__.pydata(ordemofor demo data). - The file is loaded after any file it references.
- The XML ID actually exists in the file (no typo).
# __manifest__.py
'data': [
'security/ir.model.access.csv', # always first
'data/sequence.xml', # before anything that uses sequence
'views/menu.xml', # before actions that use menus
'views/sale_order_views.xml', # references sequence + menu
],
Why This Happens
Odoo loads data files in the order listed in __manifest__.py. Each file's records get external IDs (entries in ir_model_data). When a later record references an earlier one, Odoo looks the reference up by external ID. The four common failures:
- Order wrong. File B references something defined in File A, but A is listed after B. The reference resolves to nothing.
- Cross-module reference, dependency missing. Your file references
sale.action_quotationsbutsaleis not in yourdepends. Odoo never loaded the referenced data. - Typo or rename. Someone renamed an XML ID and forgot to update references. The reference now points to a nonexistent record.
noupdate=Trueflag prevented an updated record from being re-imported, leaving stale references in the database after a code change.
Cause 4 is the silent killer. The XML import "succeeds" because Odoo skipped the noupdate-flagged record entirely, but downstream references then fail.
Step-by-Step Diagnosis
1. Identify the missing record. The error names it. Note module and id.
2. Find its definition.
grep -r "id=\"sequence_priority_quote\"" /opt/odoo/addons/sale_extension/
If grep finds zero hits, the record is genuinely not in the source — typo or never created.
3. Find every reference to it.
grep -r "sale_extension.sequence_priority_quote" /opt/odoo/addons/
Every hit is a place that depends on this XML ID. Make sure each is loaded after the definition.
4. Check __manifest__.py data order. The file containing the definition must come before any file containing a reference.
5. Check ir_model_data directly:
SELECT id, module, name, model, res_id
FROM ir_model_data
WHERE module = 'sale_extension' AND name = 'sequence_priority_quote';
Zero rows means the record was never imported. One row means it was, and your reference path is wrong.
6. Check noupdate history. If the record exists in the database but the new XML changes were not picked up:
SELECT noupdate FROM ir_model_data
WHERE module = 'sale_extension' AND name = 'sequence_priority_quote';
true means Odoo will skip it on update — which is fine for user-modifiable data but a trap if you are trying to ship a fix.
Permanent Fix
For order issues, fix __manifest__.py. Group data files into stable phases:
'data': [
# 1. Security (always first)
'security/ir.model.access.csv',
'security/security.xml',
# 2. Data records and sequences
'data/sequence.xml',
'data/mail_template.xml',
'data/cron.xml',
# 3. Views (depend on data and security)
'views/menu.xml',
'views/sale_order_views.xml',
'views/wizard_views.xml',
# 4. Reports
'reports/sale_priority_report.xml',
],
This ordering covers 95 percent of cases. When in doubt, put data before views, views before reports.
For cross-module references, fix depends:
{
'name': 'Sale Extension',
'depends': [
'sale', # for sale.action_quotations
'sale_management', # for sale.action_orders
'mail', # if you reference mail templates
],
...
}
Every external XML ID in your data implies a depends entry.
For typos and renames, the safe rename pattern is:
<!-- Old id is removed; add an ir.model.data record manually to bridge if needed -->
<record id="sequence_priority_quote_v2" model="ir.sequence">
<field name="name">Priority Quote</field>
<field name="prefix">PQ-</field>
</record>
If existing databases reference the old id, write a pre-migration.py that renames it:
# migrations/17.0.1.1.0/pre-migration.py
def migrate(cr, version):
cr.execute("""
UPDATE ir_model_data
SET name = 'sequence_priority_quote_v2'
WHERE module = 'sale_extension'
AND name = 'sequence_priority_quote'
""")
For noupdate issues, decide deliberately:
<!-- For records the user should be able to edit and that should not be overwritten -->
<data noupdate="1">
<record id="default_priority_quote" model="sale.order">
<field name="name">Sample Priority Quote</field>
</record>
</data>
<!-- For records that should always reflect the module's current source -->
<data>
<record id="sequence_priority_quote" model="ir.sequence">...</record>
</data>
If you need to force-update a previously-noupdate record, use a pre-migration:
def migrate(cr, version):
cr.execute("""
UPDATE ir_model_data SET noupdate = false
WHERE module = 'sale_extension' AND name = 'mail_template_priority'
""")
Then the next -u sale_extension will refresh that record from XML.
How to Prevent It
- Lint XML IDs in CI. Write a script that walks every
data:entry in every manifest, parses each XML, builds the full ID graph, and fails the build if any reference is unresolved. Catches 90 percent of these errors before they hit any database. - One file per concern. Keep sequences, menus, actions, and views in separate files. Mixing types in one file makes ordering bugs more likely.
- Always declare
dependsfor every external reference. If you referencesale.x, depend onsale. - Use noupdate consciously. Default to
noupdate="0"(or omit, which is the same). Reservenoupdate="1"for user-customizable data (default mail templates, default sequences) where overwriting would destroy customer changes. - Test on a fresh database. Install your module on a clean database in CI. Any "data not found" surfaces immediately. ECOSIRE's Odoo customization team runs this for every PR.
- Tag data files with an order comment. A one-line comment at the top of each XML data file ("requires: sequence.xml, menu.xml") helps the next developer order new files correctly.
Related Errors
- Module installation error: version mismatch — install-time sibling.
- noupdate flag overwriting customer data — what happens when noupdate is wrong in the other direction.
- Module not loadable with traceback — what happens when the install/update sequence aborts.
- Upgrade aborted, database corrupted — worst-case outcome of cascading data load failures.
Frequently Asked Questions
Why does Odoo not abort on every "data not found"?
Some references are non-critical (a menu's parent_id pointing at a removed menu just makes the menu top-level). Odoo logs and continues for those. Other references (a view's inherit_id) are fatal. The behaviour depends on the field. Check your log for warnings as well as errors — silent skips often have a WARNING earlier.
What is the difference between data: and demo: in the manifest?
data: files load on every install and update. demo: files load only when --load-demo-data is set. Demo data should never be referenced by production data — keep them strictly separate. Mixing them is a frequent source of "data not found" in production where demo data was never loaded.
How do I delete an XML record cleanly?
Add it to the --update cycle once with the record removed from the file (Odoo will detect the missing definition and offer to delete based on delete_orphan=true settings), or use <delete model="..." search="[]"/> for explicit cleanup. Hand-deleting ir_model_data rows is a last resort — Odoo can recreate them on the next update.
Can I reference XML IDs across modules without depends?
No. The depends graph determines load order. If you reference sale.x, your module must load after sale, which means sale is in your depends. Trying to skip this saves nothing and breaks installation order.
Need help with a tricky Odoo error? ECOSIRE's Odoo experts have shipped 215+ modules — get expert help.
Rédigé par
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
ECOSIRE
Transformez votre entreprise avec Odoo ERP
Implémentation, personnalisation et assistance expertes d'Odoo pour rationaliser vos opérations.
Articles connexes
Comment ajouter un bouton personnalisé à une vue de formulaire Odoo (2026)
Ajoutez des boutons d'action personnalisés aux vues de formulaire Odoo 19 : méthode d'action Python, héritage des vues, visibilité conditionnelle, boîtes de dialogue de confirmation. Testé en production.
Comment ajouter un champ personnalisé dans Odoo sans Studio (2026)
Ajoutez des champs personnalisés via le module personnalisé dans Odoo 19 : héritage de modèle, extension de vue, champs calculés, décisions magasin/non-magasin. Code d'abord, contrôle de version.
Comment ajouter un rapport personnalisé dans Odoo à l'aide d'une mise en page externe
Créez un rapport PDF de marque dans Odoo 19 à l'aide de web.external_layout : modèle QWeb, format papier, liaison d'action. Avec logo imprimé + remplacements de pied de page.