Skip to content
Troubleshooting

Odoo Worker Timeout: Fixing limit_time_real Errors

2026-07-16 · 9 min read

Last updated: 2026-08-09

WorkerTimeoutError
CRIT ... Worker (pid=XXXX) timeout after XXXs, killing it

Short answer: a single request took longer than limit_time_real seconds to complete, so Odoo's own worker-supervision process killed the worker handling it to protect the rest of the server. The request that was running dies; it does not queue or retry automatically.

What limit_time_real actually does

Odoo's multiprocessing worker model includes a watchdog. Each request has a real-world wall-clock time budget (limit_time_real) and a CPU time budget (limit_time_cpu). If either is exceeded, the worker process handling that request is killed and a fresh one is spawned to replace it. This is a deliberate safety mechanism, a single runaway request (an unbounded report query, a bad loop in a custom module) should not be allowed to hang a worker forever and slowly starve the whole server of available workers.

This matters because Odoo's prefork worker model has a fixed pool. If you run workers = 4, there are exactly 4 processes available to handle every incoming HTTP request across every user. A single request stuck forever, with no watchdog, would permanently remove one of those 4 workers from service. With enough stuck requests over time, the whole instance eventually has no workers left to answer any request at all, which presents to end users as total unresponsiveness, not a clean error. The timeout mechanism trades "this one request fails" for "the whole server doesn't quietly die."

There's also a related but separate setting, limit_request, which restarts a worker after it has handled a certain number of requests, regardless of timing, as a defence against slow memory leaks in long-lived Python processes. It's not the cause of a WorkerTimeoutError, but it's worth knowing it exists so you don't confuse a scheduled worker recycle in the logs with an actual timeout event.

limit_time_real vs limit_time_cpu: not the same budget

These two settings get confused constantly, and mixing them up leads people to raise the wrong one. limit_time_cpu measures only actual CPU execution time, it excludes time spent waiting on the database, disk I/O or network calls. limit_time_real measures true wall-clock time, everything included. A request that spends most of its time waiting on a slow PostgreSQL query will blow through limit_time_real long before it comes close to limit_time_cpu, because waiting isn't CPU work. That's why the fix below always gives limit_time_real meaningfully more headroom than limit_time_cpu, they are not measuring the same thing and should not be set to the same number.

Causes, ranked by how often they're actually it

1. A genuinely slow report or export

Large PDF report generation or big data exports are the most common legitimate cause. Check which endpoint was being hit when the timeout happened:

sudo journalctl -u odoo | grep -B5 "timeout after" | grep -i "POST|GET"

If it's consistently the same report (a large aged receivables report, a big BOM explosion, a year-end inventory valuation), the report itself is legitimately expensive and the timeout is doing its job correctly, the fix is raising the limit with headroom, or breaking the report into a batched/background job if one exists for it.

2. A missing database index causing a slow query

sudo -u postgres psql your_db -c "SELECT query, mean_exec_time, calls FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10;"

Requires the pg_stat_statements extension enabled (CREATE EXTENSION IF NOT EXISTS pg_stat_statements; as a superuser, then a PostgreSQL restart to pick up the config if it's not already loaded via shared_preload_libraries). If one query dominates mean execution time and is called from the endpoint that's timing out, that's your actual cause, not the timeout value itself. Confirm with EXPLAIN ANALYZE on the offending query and look for a sequential scan (Seq Scan) on a large table where an index scan would be expected, that's the single most common root cause behind "random" timeout reports on otherwise healthy servers.

3. limit_time_real set too low for legitimate workloads

The Odoo default is 120 seconds for limit_time_real in many reference configs, which is genuinely too tight for heavy report generation or bulk imports on larger datasets. Check your current value:

grep limit_time /etc/odoo/odoo.conf

If the value is unset, Odoo falls back to its own built-in default, don't assume "not in the config file" means "no limit", check the version-specific default in the Odoo documentation for your exact version rather than guessing.

4. An infinite loop or runaway recursion in a custom module

If the same endpoint times out consistently regardless of data size or server load, and it's backed by custom code, suspect a logic bug rather than a genuinely large workload. Check for unbounded while loops or recursive write() calls that re-trigger themselves via automated actions. A specific pattern worth checking: an automated action or server action that writes to the same model it's triggered on, without a guard condition, can create a write-triggers-write loop that looks like a slow request but is actually an infinite one that would never finish even with an unlimited timeout.

5. Concurrent workers all hitting the same slow path at once

Sometimes an endpoint is fine in isolation but times out only under concurrent load from several users, because each request is waiting on a database lock held by another. Check for lock contention during the timeout window:

sudo -u postgres psql your_db -c "SELECT pid, wait_event_type, wait_event, query FROM pg_stat_activity WHERE wait_event IS NOT NULL;"

If you see many rows waiting on the same lock type, the fix is addressing the lock contention (often a long-running write transaction from a batch job holding a row lock longer than expected), not raising the timeout.

The fix

For a genuine heavy-workload case, raise both limits with headroom:

# /etc/odoo/odoo.conf
limit_time_cpu = 600
limit_time_real = 1200
# Give real time meaningfully more headroom than CPU time --
# real time includes I/O wait (database, disk, network), CPU time doesn't.

Restart after any config change:

sudo systemctl restart odoo

If the cause is a missing index, add it and re-test before touching the timeout values at all, raising the timeout without fixing a slow query just delays the same problem to a larger dataset later. A reasonable rule: only raise the timeout permanently for workloads you've confirmed are legitimately that expensive (large reports, bulk imports), not as a blanket fix for the first timeout you see.

How to verify

sudo journalctl -u odoo -f
# trigger the previously-failing report/export again and watch for a clean completion, no WorkerTimeoutError

Also confirm workers recover cleanly and don't pile up:

ps aux | grep odoo-bin | wc -l
# should match your configured workers count, not grow unbounded

If you added a database index as the fix, re-run the same pg_stat_statements query from cause 2 after a day of normal traffic and confirm the mean execution time for that query dropped meaningfully, not just that the timeout stopped firing once, a genuinely fixed query should show a clear before/after difference in the stats.

Still stuck

If timeouts happen across many different endpoints, not one specific slow report, the problem is more likely server-wide resource starvation (undersized shared_buffers, too few workers for your concurrent user count, or PostgreSQL connection exhaustion) rather than a single query. See our workers formula post and PostgreSQL tuning guide. If timeouts only happen at specific times of day (month-end, a nightly cron window), check what else is scheduled to run at that time, contention with a backup job or a heavy scheduled action is a common and easy-to-miss cause once you know to look at the clock, not just the code.

Sources: Odoo documentation.

Talal Yousaf, Senior DevOps Engineer at DevFusion Tech. 70+ Odoo deployments across Community and Enterprise, versions 13 through 19. More about DevFusion · LinkedIn

Published 2026-07-16 · updated 2026-08-09

ShareLinkedInXWhatsApp

Hit the same wall in production?

Tell us what you're running and where it breaks. A 30 minute call, no pitch deck, and a straight answer on whether we can help.