Odoo Scheduled Actions (Cron) Not Running: How to Fix
Odoo scheduled actions (cron jobs) not running
Also searched as
- Odoo cron job stuck fix
- Odoo automated action not firing
- Odoo ir.cron not executing
- Odoo scheduled action next execution date not updating
Short answer
Odoo scheduled actions usually stop firing because no cron worker is available (workers or max-cron-threads misconfigured), the action is set inactive, or an earlier job crashed and left an ir.cron row stuck in the running state, which blocks the queue. Check Settings > Technical > Automation > Scheduled Actions first, then the server config and the ir.cron table.
Applies to: Odoo 14 through 18, Community and Enterprise editions
Fix scheduled actions that are not running
- 1Open Settings > Technical > Automation > Scheduled Actions and confirm the Active toggle is on for the affected job.
- 2Check the Next Execution Date; if it is in the future the job is simply waiting, not broken.
- 3Look at the server log at startup for a line mentioning cron threads or workers being registered.
- 4Confirm workers and max-cron-threads in odoo.conf (or command-line flags) are set correctly; a single-process dev server still needs max-cron-threads greater than 0.
- 5Query the ir_cron table for rows with state = 'running' that are older than expected; these block subsequent runs of the same job.
- 6Use the Run Manually button on the scheduled action to test the job outside the normal scheduler loop.
- 7Check for exceptions raised inside the job's Python method; Odoo can swallow the error and reschedule without an obvious log entry depending on version.
- 8Restart the Odoo service so any stale PostgreSQL advisory lock tied to cron processing is released.
- 9On Odoo.sh or Odoo Online, confirm cron is not throttled or disabled for the current plan or in a duplicate/staging database.
Why scheduled actions stall
Odoo's scheduler is a background loop, not an OS-level cron daemon. A dedicated process (or thread, depending on deployment) polls the ir_cron table for jobs whose nextcall is due, then executes them one at a time per worker slot reserved for cron. If that polling process never starts, or has zero threads allocated to cron work, nothing on the Scheduled Actions list will ever fire, even though the records look completely normal in the UI.
The two most common root causes are a deployment configuration that never allocated cron capacity, and a single job that failed mid-execution and left its ir_cron row marked as running. Odoo's default locking behavior treats a running row as still in progress, so the scheduler skips scheduling it again until that row is cleared, which can make it look like every job stopped rather than just one.
The workers and max-cron-threads relationship
In multiprocessing deployments, --workers controls the number of HTTP worker processes, while --max-cron-threads controls how many of those processes are reserved for cron execution, and the two are independent settings. Setting workers to 0 puts Odoo in single-threaded development mode; cron still runs there through a separate thread as long as max-cron-threads is not explicitly set to 0.
A frequent misconfiguration is copying a production odoo.conf into a new environment and accidentally dropping the max-cron-threads line, or setting it to 0 to 'save resources,' which silently disables all scheduled actions in that instance without any error in the UI.
; odoo.conf workers = 4 max_cron_threads = 2
Diagnosing via the ir_cron table
When the UI does not explain enough, query ir_cron directly (with database access or via the Odoo shell) to see real state, not just what the form view shows. Jobs stuck in running for longer than their expected duration are the clearest sign of a blocking crash.
SELECT id, cron_name, active, state, nextcall, priority FROM ir_cron ORDER BY nextcall;
-- reset a job stuck in 'running' after a confirmed crash UPDATE ir_cron SET state = 'done' WHERE id = 42;
Exceptions that fail silently
A job that raises an exception inside its Python method is caught by Odoo's cron runner, logged, and rescheduled according to its interval, which means the job appears to 'run' on schedule while never completing its actual work. This is easy to miss if log monitoring only checks for process crashes rather than traceback entries tied to a specific cron job name.
Reproducing the failure with Run Manually, or calling the underlying model method directly from an Odoo shell session, surfaces the traceback immediately and is far faster than waiting for the next scheduled interval to see the same failure repeat in the logs.
Common pitfalls
- !Assuming --workers 0 fully disables cron; it does not by itself, max-cron-threads is the setting that controls cron capacity.
- !Clicking Run Manually and treating a successful manual run as proof the scheduler will now pick the job up automatically, when the Active flag is still off.
- !Not checking for a stuck ir_cron row in state 'running' after a server crash or forced restart, which silently blocks that job going forward.
- !Confusing company timezone settings with server timezone when reading Next Execution Date, leading to false alarms about a job being 'late'.
- !Assuming Odoo.sh and Odoo Online behave identically to self-hosted regarding cron throttling; hosted plans can rate-limit background jobs.
- !Editing a scheduled action's interval in the UI without noticing validation errors on the Numbers of calls or Interval Number fields that silently keep the old value.
How an ERP-grounded AI assistant handles this
ERPray, grounded on a specific Odoo instance's odoo.conf, deployment topology, and the ir_cron table itself, can answer 'why did this scheduled action not run last night' by checking active status, worker allocation, and stuck rows in one pass instead of a support engineer stepping through each config file and log by hand. It still needs the operator to confirm any state change back to the database, since resetting a stuck cron row is a production write, not a read-only lookup.
Frequently asked questions
How do I know if Odoo cron workers are actually running?
Check the server startup log for a line referencing cron threads or worker registration, and confirm max_cron_threads in your config is greater than 0. You can also watch the ir_cron table: if nextcall values keep advancing on active jobs, the scheduler is polling correctly.
Can I run scheduled actions without dedicated cron workers?
Yes. Even with --workers 0 in single-process mode, Odoo runs a background thread for cron as long as max-cron-threads is not set to 0. It is less scalable than a dedicated worker but works fine for small instances and development.
Why does my cron job run once and then stop firing?
The most common cause is the job raising an exception and leaving its ir_cron row in the running state, which blocks it from being picked up again. Check the row's state column and reset it to done after confirming the underlying error is fixed.
How do I manually reset a stuck cron job?
Update the ir_cron row's state column back to done via SQL or the Odoo shell, for example UPDATE ir_cron SET state = 'done' WHERE id = <job id>. Only do this after confirming the previous run actually crashed rather than being genuinely in progress.
Related
How to Create an Import Scenario in Acumatica
Import scenarios in Acumatica live under System Integration > Import Scenarios: you pick a source (a file or a Generic Inquiry), map each source field to a target DAC field on the destination screen, then run or schedule the scenario. A small sample file run through the wizard first makes the field mapping step far faster than mapping blind.
How-toHow to Run a Physical Inventory Count in Sage X3
Sage X3 physical counts run through Stock > Counts: create a count session scoped to a site, category, or zone, generate the count sheet to freeze theoretical quantities, enter counted quantities, review the variance report, then validate to post stock and valuation adjustments. Blocking movements against the counted locations during the window keeps the variance meaningful.
Error fixFix Odoo AccessError: You Are Not Allowed to Access Records
Odoo raises AccessError when a user's security groups do not grant a CRUD right on a model through ir.model.access.csv, or when an ir.rule record rule filters the specific record out for that user or company. Fix it by adding the user to the group named in the error, editing the model's access rights, or reviewing the record rule's domain under Settings > Technical > Security.
How-toHow to Set Up Business Events and Actions in IFS
IFS Business Events fire when a defined condition on a Logical Unit is met, such as a new record, an update, or a specific attribute changing, and Actions subscribe to those events to send email, invoke a workflow, or call an external service. Both are configured in Solution Manager (or the Events and Actions work area in IFS Cloud) without requiring custom PL/SQL for most standard cases.
How-toHow to Create a Generic Inquiry in Acumatica
Generic Inquiries (GI) are Acumatica's no-code query builder for joining tables, adding filters, and exposing the result as an inquiry screen, dashboard data source, or API endpoint. Open System > Customization > Generic Inquiry, add your base table, define joins and conditions, then run and save. No SQL or customization project is required.
How-toHow to Void a Check in Sage 100
Sage 100 voids checks through Accounts Payable > Main > Check and Payment Voiding. Select the bank code, choose the check number, and Sage reverses the Cash Disbursements Journal entry and reopens the invoice it paid. A check that already cleared in Bank Reconciliation or falls in a closed period needs an extra step before it can be voided cleanly.
AI for ERPAI for Odoo Manufacturing That Reads Your Own Database, Not a Vendor's Cloud
Add grounded, natural-language AI to Odoo Manufacturing without sending MRP, BoM, or work order data to a public model. On-prem or private cloud, ORM-native.
Stuck on Odoo (Community and Enterprise)?
Talk to engineers who work inside Odoo (Community and Enterprise) every week, and who build private AI that answers these questions from your own ERP data.