Bu makale şu anda yalnızca İngilizce olarak mevcuttur. Çeviri yakında eklenecektir.
After an Odoo major version upgrade, a database starts behaving strangely. Fields you do not recognize appear on models. Old fields are missing from the form. Some users see the new layout, others see the old. The server log shows a parade of:
WARNING: Field 'x_studio_old_field' on model 'sale.order' not found, ignored
ERROR: View 'sale.order.form.inherit.foo' has invalid arch (column does not exist)
This is registry-versus-database state drift after a partially-failed upgrade. The data is intact but Odoo's metadata about which models, fields, and views exist has gone out of sync. This guide walks through a controlled recovery for Odoo 17.0/18.0/19.0.
Quick Fix
The fastest sanity-restorer is -u all against the upgraded database with a clean worker pool:
sudo systemctl stop odoo
sudo find /opt/odoo -name "__pycache__" -type d -exec rm -rf {} +
sudo -u odoo /opt/odoo/odoo-bin -c /etc/odoo/odoo.conf \
-d <db> -u all --stop-after-init --no-http
sudo systemctl start odoo
If this clears the warnings, you were lucky — only the bytecode and registry caches were stale. If the warnings persist, you have real metadata drift and need the full recovery below.
Why This Happens
Odoo's metadata lives in three places: Python source code (the actual classes), the ir_model / ir_model_fields / ir_ui_view tables (registered metadata), and the running registry (in-process Python). After an upgrade, all three should agree. Drift happens when:
- The upgrade aborted partway through
-u all— half the modules upgraded, half did not, and the registry is mixed. - A custom module's manifest version was not bumped, so Odoo skipped its migration scripts even though the code changed.
- Studio fields added in the previous version are not in the new version's source code — they linger as ghost fields in
ir_model_fields. - A module was renamed (or a model was renamed) without an
openupgrademigration script. The DB has the old name, the source has the new. - Inherited views (
ir.ui.view) reference removed fields. The view loads, fails arch validation, gets disabled, and the form looks broken.
Step-by-Step Diagnosis
1. Confirm no module is in transitional state.
SELECT name, state FROM ir_module_module
WHERE state NOT IN ('installed', 'uninstalled', 'uninstallable');
Any rows here mean the upgrade did not finish. Re-run with -u all.
2. Find ghost fields. Fields that exist in the database but not in the source:
SELECT m.model, f.name
FROM ir_model_fields f
JOIN ir_model m ON m.id = f.model_id
WHERE f.state = 'manual' -- studio/manual fields
AND f.modules IS NULL; -- not declared by any module
Ghost fields are usually safe to drop, but back up first.
3. Find broken inherited views.
SELECT id, name, model, arch_db
FROM ir_ui_view
WHERE active = false AND inherit_id IS NOT NULL
ORDER BY write_date DESC LIMIT 20;
Recently-disabled inherited views are your top candidates.
4. Compare expected vs actual fields per model. In an Odoo shell:
expected = set(env['sale.order']._fields.keys())
actual = set(r['name'] for r in env['ir.model.fields']
.search_read([('model', '=', 'sale.order')], ['name']))
print('In DB but not source:', actual - expected)
print('In source but not DB:', expected - actual)
Either set being non-empty is a sign of drift.
5. Check ir_model_data for orphans.
SELECT module, name, model
FROM ir_model_data
WHERE module NOT IN (SELECT name FROM ir_module_module WHERE state = 'installed');
Orphan ir.model.data references break dependency resolution.
Permanent Fix
Step 1 — Snapshot the database. Always. pg_dump <db> > pre-fix.sql.
Step 2 — Drop ghost fields:
ghost_fields = env['ir.model.fields'].search([
('state', '=', 'manual'),
('modules', '=', False),
])
for f in ghost_fields:
_logger.warning("Dropping ghost field %s.%s", f.model, f.name)
f.unlink()
unlink() on ir.model.fields properly drops the column and all dependent metadata.
Step 3 — Re-enable broken inherited views with their views fixed. For each disabled inherit:
view = env['ir.ui.view'].browse(view_id)
# Inspect view.arch_db, fix the field reference, then:
view.write({'arch': fixed_arch, 'active': True})
If a view is no longer needed, archive it explicitly rather than letting it sit broken.
Step 4 — Run -u all once more with the cleaned metadata:
sudo -u odoo /opt/odoo/odoo-bin -c odoo.conf -d <db> -u all --stop-after-init
Step 5 — Verify with the OCA module checker:
pip install click-odoo-contrib
click-odoo-update -d <db>
It walks the registry and reports drifts that survived the upgrade.
For complex cases, the OCA openupgrade framework includes scripts that automate field renames, model renames, and module merges across major version bumps. ECOSIRE's Odoo migration team uses openupgrade for every version migration — manually replicating its checks on production is rarely worth it.
How to Prevent It
- Never run
-u allon production without a backup. And never run it when modules are in a transitional state. Snapshot, then upgrade, then verify. - Bump custom module versions on every release. A version bump triggers Odoo's migration runner. Without it, your
migrations/directory is silently ignored. - Inventory Studio fields before upgrading. Studio adds
manualfields that are not in any module's source. List them, decide which to keep as Python code, which to delete. - Archive instead of delete inheritance. Never
unlinkanir.ui.viewdirectly — Odoo may reactivate or reference it during upgrade. Archive (active=False) and review. - Use openupgrade. It exists for exactly this reason. The community has written hundreds of migration helpers; your job is to assemble them, not rewrite them.
- Practice on staging. Restore production into staging, run the upgrade end-to-end, run a smoke test, fix every warning before touching production. The "first attempt cost" goes down to almost zero on the second try.
Related Errors
- Upgrade aborted, database corrupted — the worse cousin of this bug.
- KeyError: environments_cache after upgrade — registry / cache reset.
- Module installation error: version mismatch — a frequent trigger of partial upgrades.
- Relational fields broken after upgrade — symptom that lives in the same family.
Frequently Asked Questions
Is dropping ghost fields safe?
Generally yes for state='manual' fields with no modules declaration — they are leftovers from Studio customizations or older module versions. Always snapshot the database first; once dropped, the column is gone and the data with it. If a Studio customization was important, export it before dropping.
Why does Odoo keep loading the broken view?
Odoo loads inherited views in module-dependency order at every server start. A broken view fails arch validation, gets active=False, and is logged but not deleted. On the next start the same thing happens. The loop only ends when you fix or archive the view.
My drift is in ir_model_data (XML IDs). What do I do?
Use the --xmlid flag with click-odoo-update, or write a one-off cleanup that nulls the orphan rows. Orphan XML IDs do not crash anything but they break the next upgrade — clean them as part of the post-upgrade pass.
Can I just restore from backup and try again?
Yes, and often this is the right call if you are within the maintenance window. Restore, fix the manifest version bumps and ghost-field cleanup before running -u all, then upgrade against the cleaned snapshot. Two passes on staging beats fixing live.
Need help with a tricky Odoo error? ECOSIRE's Odoo experts have shipped 215+ modules — get expert help.
Yazan
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
Odoo ERP ile İşinizi Dönüştürün
Operasyonlarınızı kolaylaştırmak için uzman Odoo uygulaması, özelleştirme ve destek.
İlgili Makaleler
Odoo Form Görünümüne Özel Düğme Nasıl Eklenir (2026)
Odoo 19 form görünümlerine özel eylem düğmeleri ekleyin: Python eylem yöntemi, görünüm devralma, koşullu görünürlük, onay diyalogları. Üretimde test edilmiştir.
Odoo'da Studio Olmadan Özel Alan Nasıl Eklenir (2026)
Odoo 19'daki özel modül aracılığıyla özel alanlar ekleyin: model mirası, görünüm uzantısı, hesaplanan alanlar, mağaza/depo dışı kararlar. Kod öncelikli, sürüm kontrollü.
Odoo'da Harici Düzeni Kullanarak Özel Rapor Nasıl Eklenir?
Web.external_layout'u kullanarak Odoo 19'da markalı bir PDF raporu oluşturun: QWeb şablonu, paperformat, action bağlama. Baskı logosu + altbilgi geçersiz kılmalarıyla.