Zum Hauptinhalt springen

Odoo KeyError-Umgebungen_cache nach dem Upgrade – Fix-Anleitung

Drücken Sie nach dem Upgrade von Odoo 17 auf 18 oder 18 auf 19 den KeyError-Umgebungscache? Korrigieren Sie veraltete Assets, Registry und Worker-Status in drei Schritten in der Produktion.

E
ECOSIRE Research and Development Team
|4. Mai 20266 Min. Lesezeit1.2k Wörter|

Dieser Artikel ist derzeit nur auf Englisch verfügbar. Die Übersetzung folgt bald.

You finish an Odoo upgrade — 17.0 to 18.0, or 18.0 to 19.0 — restart workers, and the first request crashes with this in the log:

You finish an Odoo upgrade — 17.0 to 18.0, or 18.0 to 19.0 — restart workers, and the first request crashes with this in the log:

Traceback (most recent call last):
  File "/odoo/odoo/api.py", line 489, in __getitem__
    return self.envs[key]
KeyError: 'environments_cache'

Sometimes the variant is KeyError: ('environments_cache', ...) or a similar internal cache key. The error originates inside Odoo's API layer when worker processes carry over state from the previous version's bytecode and class hierarchy. The fix is mechanical, but skipping a step turns a 5-minute reset into a 5-hour outage.

Quick Fix

Stop every Odoo process, clear caches, and restart cleanly:

# 1. Stop all workers
sudo systemctl stop odoo

# 2. Clear Python bytecode and assets
sudo find /opt/odoo -name "__pycache__" -type d -exec rm -rf {} +
sudo find /opt/odoo -name "*.pyc" -delete

# 3. Clear web assets cache (Odoo regenerates on first request)
psql -U odoo -d <db> -c "DELETE FROM ir_attachment WHERE url LIKE '/web/assets/%';"

# 4. Restart with --update=all on the upgrade target
sudo -u odoo /opt/odoo/odoo-bin -c /etc/odoo/odoo.conf -d <db> -u all --stop-after-init

# 5. Start normally
sudo systemctl start odoo

Ninety percent of KeyError: 'environments_cache' cases clear after these five steps.

Why This Happens

Odoo's ORM keeps a per-cursor Environment cache that maps (uid, context, su) tuples to ORM environments. The internal key changed between versions (notably 16.0 to 17.0, and 18.0 to 19.0). The error fires when:

  1. Stale .pyc bytecode from the old version is still on disk and a worker imports it instead of the new source.
  2. A long-running worker survived the upgrade restart (gunicorn/longpolling stuck on a request) and is running 17.0 code while talking to an 18.0-migrated database.
  3. An ir.attachment asset baked from the old version is being served and references removed APIs.
  4. The PostgreSQL session is holding a prepared statement from the old schema (rare, but happens with PgBouncer in transaction mode).
  5. A custom module uses a private API that moved, like self.env._cache or self.env.cache.get_special() — the import succeeds but the attribute access blows up.

Step-by-Step Diagnosis

1. Confirm all workers are stopped.

ps -ef | grep odoo-bin | grep -v grep

You should see zero rows. If you see any, kill them with sudo kill -9 <pid>. SystemD's stop sometimes leaves zombie longpolling workers.

2. Check for stale bytecode.

ls /opt/odoo/odoo/__pycache__/api.cpython-* 2>/dev/null

If the file's mtime is older than your upgrade, you have stale bytecode.

3. Inspect the registry. With Odoo stopped:

psql -d <db> -c "SELECT module, state FROM ir_module_module WHERE state IN ('to upgrade', 'to install', 'to remove');"

Any row in a transitional state means the upgrade did not finish. Re-run -u all --stop-after-init until this query returns zero rows.

4. Check the assets cache.

SELECT count(*) FROM ir_attachment WHERE url LIKE '/web/assets/%';

If non-zero after a major version upgrade, those assets are stale. Delete them; Odoo regenerates on the next request.

5. Test with a fresh worker. Start a single instance with --workers=0 (no preforking) and hit the login page. If it works in single-worker mode, your worker pool was carrying stale state.

Permanent Fix

For a clean, repeatable upgrade flow, codify the reset:

#!/usr/bin/env bash
# upgrade_post_steps.sh — run after every Odoo major upgrade
set -euo pipefail

DB=$1
ODOO_DIR=/opt/odoo
ODOO_USER=odoo

systemctl stop odoo
sleep 5  # let longpolling drain

# Kill stragglers
pkill -9 -u "$ODOO_USER" || true

# Clear bytecode in odoo and addons
find "$ODOO_DIR" -name "__pycache__" -type d -exec rm -rf {} + 2>/dev/null || true
find "$ODOO_DIR" -name "*.pyc" -delete 2>/dev/null || true

# Clear web assets — Odoo regenerates
sudo -u postgres psql -d "$DB" -c \
  "DELETE FROM ir_attachment WHERE url LIKE '/web/assets/%';"

# Re-run -u all to settle the registry
sudo -u "$ODOO_USER" "$ODOO_DIR/odoo-bin" \
  -c /etc/odoo/odoo.conf -d "$DB" -u all --stop-after-init --no-http

systemctl start odoo

Run this script as the last step of every major upgrade. ECOSIRE's Odoo migration team ships a hardened version of this with every customer migration.

For the third-party-module variant of the bug (cause 5 above), the durable fix is in code:

# WRONG — uses private API that moved between versions
val = self.env.cache.get_special(self, 'partner_id')

# RIGHT — use the public API; survives major version upgrades
val = self.partner_id  # ORM handles caching internally

How to Prevent It

  • Always run -u all --stop-after-init after a major version upgrade, not just -u <module>. Module-scoped upgrades skip the global registry rebuild that flushes cross-module caches.
  • Bake the bytecode wipe into your deploy script. Stale .pyc is the root cause of more upgrade incidents than any other single factor.
  • Use systemd's KillMode=mixed in your odoo.service to ensure all child workers die when the unit stops. The default leaves workers running.
  • Monitor ir.attachment asset count post-upgrade. A sudden 10x increase means the new version is being served alongside the old assets — clean them out.
  • Keep your custom modules off private APIs. Anything starting with _ (like env._cache) can move between versions without notice. Stick to documented APIs and your modules survive every major upgrade.
  • Test the upgrade on a restored production copy first. Always. Every time. The cache reset that solves staging may need a different sequence on production with a different worker count and asset volume.

Frequently Asked Questions

Why does the error not appear immediately, only on the second or third request?

Odoo's worker pool spins up lazily. The first few requests hit fresh workers that load the new code; later requests can land on workers that imported stale bytecode at process start, before your reset finished. Restart with --workers=0 once after the upgrade to flush, then return to multi-worker mode.

Should I drop and re-import the database?

No. The database is fine — it migrated cleanly. The error lives in the Python process state, not on disk. A drop-and-restore is the wrong fix and costs you hours of downtime for nothing.

Does this happen on point releases (17.0 → 17.0+)?

Rarely. Patch-level updates do not change the cache key shape, so bytecode reuse is safe. The error is almost exclusively a major-version upgrade artifact.

My PgBouncer is in transaction mode — does it cause this?

It can. PgBouncer in transaction mode pools sessions, and a session can carry server-side prepared statements from before the upgrade. After every major Odoo upgrade, restart PgBouncer along with Odoo. Make this part of the deploy script.


Need help with a tricky Odoo error? ECOSIRE's Odoo experts have shipped 215+ modules — get expert help.

Teilen:
E

Geschrieben von

ECOSIRE Research and Development Team

Technische Redaktion

Das Team für technische Redaktion bei ECOSIRE schreibt über Odoo ERP, E-Commerce mit Shopify, KI-Agenten, Analysen mit Power BI, Automatisierung mit GoHighLevel und Best Practices für Unternehmenssoftware. Unsere Leitfäden helfen Unternehmen, fundierte Technologieentscheidungen zu treffen.

ECOSIRE

Transformieren Sie Ihr Unternehmen mit Odoo ERP

Kompetente Odoo-Implementierung, Anpassung und Support zur Optimierung Ihrer Abläufe.