Cet article est actuellement disponible en anglais uniquement. Traduction à venir.
Halfway through an Odoo upgrade — typically 17.0 to 18.0 or 18.0 to 19.0 — the process aborts and the log ends with something like:
ERROR: integrity violation in module 'sale_subscription'
ERROR: upgrade aborted, database state may be corrupted
The server will not start. Workers crash on registry load. Users are locked out. This is the worst-case Odoo upgrade failure, and the recovery has a specific order. Skipping a step turns a 2-hour outage into a 2-day outage. This guide is the playbook ECOSIRE's Odoo migration team uses on every escalation.
Quick Fix
Stop. Do not retry the upgrade. Restore from the pre-upgrade backup and start over with proper preparation:
sudo systemctl stop odoo
sudo -u postgres dropdb <db>
sudo -u postgres createdb <db> -O odoo
sudo -u postgres pg_restore -d <db> /backups/pre-upgrade-snapshot.dump
sudo systemctl start odoo
Now the database is at its pre-upgrade state. Diagnose what went wrong before re-attempting. Re-running -u all against a half-upgraded database compounds the corruption.
Why This Happens
The Odoo upgrade pipeline is a long-running, multi-module transaction. When it aborts mid-way, it leaves behind:
- Half-migrated tables. Some columns renamed, others not. Some constraints dropped, others still present.
- Mixed module states.
ir_module_moduleshows half the modules at the new version, half at the old. - Disabled views and broken
ir_model_data. XML IDs that point at fields that have been renamed. - Foreign-key violations. Migration scripts often disable FKs, migrate data, re-enable. If the abort happens between, FK constraints remain unenforced.
- Inconsistent
ir_attachmentweb assets. Frontend asset bundles reference fields that no longer exist.
The five common root causes:
- Custom module migration script raised an exception. A
migrations/<v>/post-migration.pyfile that assumed a column existed. - Out-of-memory. Large migrations (millions of rows in
mail.messageoraccount.move.line) OOM the worker partway. - Disk full. PostgreSQL ran out of space for the WAL log.
- Network blip. Odoo lost the database connection during a long migration.
- Manual interruption. Someone Ctrl+C'd the upgrade because it "was taking too long".
Step-by-Step Diagnosis
1. Get the full upgrade log. Find the abort line and read backwards 200 lines. The first warning or error is the actual cause; the abort itself is just the symptom.
2. Check the OS-level signals.
journalctl -u odoo --since "2 hours ago" | grep -E "(killed|memory|signal)"
dmesg | grep -i "killed process"
OOM-killer entries in dmesg are dispositive — your worker ran out of RAM.
3. Check disk and WAL state.
df -h /var/lib/postgresql
sudo -u postgres psql -c "SELECT pg_walfile_name(pg_current_wal_lsn());"
Disk above 95 percent or WAL pile-up tells you to fix the storage layer before retrying.
4. Inventory transitional modules.
SELECT name, state, latest_version
FROM ir_module_module
WHERE state IN ('to upgrade', 'to install', 'to remove');
This is your mid-flight cargo manifest. The first module in this list is usually where the abort happened.
5. Check transaction log of the migration.
grep "migrations" /var/log/odoo/odoo.log | tail -50
The last Loading migrations/<v>/<phase> line tells you exactly which script blew up.
Permanent Fix
Step 1 — Restore. Always restore from the pre-upgrade snapshot before doing anything else. Never patch a half-upgraded database. The diagnostic findings tell you what to fix on the restored copy before trying again.
Step 2 — Fix the root cause.
- Custom migration script error: read the script, find the assumption (usually "this column exists"), fix it. Test on a restored copy.
- OOM: increase
limit_memory_hardandlimit_memory_softinodoo.conf, add swap, or split the migration into batches. Long migrations sometimes need--workers=0 --max-cron-threads=0to give the entire process maximum memory. - Disk full: add storage. PostgreSQL needs roughly 2x the database size in free WAL space for a major upgrade.
- Network blip: run the upgrade on the database server itself (or via a stable VPN session, not a shaky SSH tunnel).
- Manual interruption: explain to the team that Odoo upgrades take what they take. Document an SLO ("Sale orders module always takes 45 minutes") so people stop interrupting.
Step 3 — Pre-upgrade dry run. With the cause fixed, restore a fresh copy and dry-run:
sudo -u odoo /opt/odoo/odoo-bin -c upgrade.conf \
-d <staging-db> -u all --stop-after-init --no-http \
--logfile=/var/log/odoo/upgrade-dry-run.log
Read the entire log even if exit code is 0. Warnings during dry-run become aborts during production.
Step 4 — Real upgrade with safeguards. When dry-run is clean, run the production upgrade with checkpoint commits:
# Use openupgrade's checkpoint tooling for major versions
pip install openupgradelib
sudo -u odoo /opt/odoo/odoo-bin -c production.conf \
-d <db> -u all --stop-after-init --no-http
Always tail the log. Always have the rollback pg_restore command ready in another terminal.
Step 5 — Post-upgrade verification.
SELECT count(*) FROM ir_module_module
WHERE state IN ('to upgrade', 'to install', 'to remove');
This must be 0. Then run click-odoo-update -d <db> to catch lingering drift, and run a smoke test on every critical workflow before re-opening to users.
How to Prevent It
- Backup is non-negotiable. Every Odoo production database needs daily
pg_dumpplus continuous WAL archiving. Before any upgrade, an additional fresh full backup. ECOSIRE production deploys gate on Step 3 (pre-deploy-backup.cjs) — no backup, no deploy. - Always dry-run on staging first. A staging database identical to production catches every migration script bug. The 2-hour staging cost saves a 12-hour production outage every time.
- Don't custom-write migration scripts unless you must. OCA's
openupgradeships scripts for every official module's renames and field changes. Use them. - Memory and disk plan. Know your DB size, double it, ensure you have headroom. Increase
limit_memory_*for the upgrade pass. - Maintenance window communication. Set expectations with the team. Major Odoo upgrades take hours, not minutes. Plan for it. Plan for double if it is the first major version jump in two years.
- Run from the DB server. The single biggest cause of "upgrade aborted" we see at ECOSIRE is a flaky network connection between the upgrade host and the database. SSH to the DB server (or its bastion) and run from there.
Related Errors
- Module installation error: version mismatch — pre-upgrade variant of this failure.
- KeyError: environments_cache after upgrade — what happens when the upgrade completes but state is stale.
- Model state corruption after upgrade — partial-success cousin.
- Too many PostgreSQL connections post-upgrade — first symptom users notice if the upgrade thrashes connections.
Frequently Asked Questions
Can I just retry -u all on the half-upgraded database?
No. The half-upgraded state has invalid foreign keys, partial column renames, and inconsistent ir_module_module rows. Retrying compounds the corruption. Restore, fix, retry against the clean snapshot.
How long should a major version upgrade take?
Highly variable. A small Odoo (5 GB DB, 20 modules, no Studio) upgrades in 20 minutes. A typical production system (50 GB DB, 60 modules, Studio in use) takes 2 to 4 hours. Add an hour for asset rebuild and verification. If your dry-run took 30 minutes, expect production to take 30 to 45 minutes.
Is pg_dump the right backup format for upgrades?
Use pg_dump -Fc (custom format) or pg_basebackup plus WAL archives. Plain SQL dumps are fine but slow to restore on large databases. Custom format restores in parallel with pg_restore -j 4. For databases over 100 GB, pg_basebackup is the only sane option.
Should I upgrade through every intermediate version (17 → 18 → 19) or jump directly?
Always go through the intermediates. Odoo's upgrade scripts are written assuming each version is the source. Jumping versions means writing custom migration scripts to bridge the gap. The intermediate path is slower in wall time but vastly safer in risk and effort. ECOSIRE's Odoo migration team does it this way for every customer.
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.