Skip to content

Fix Odoo Scheduled Action Errors: Cron Jobs Stopped, Stuck, or Failing

DeployMonkey Team · March 23, 2026 10 min read

The Problem

Your Odoo scheduled action (cron job) was running fine for months, then suddenly stopped. Emails are not being sent, invoices are not auto-generated, and automated tasks have silently failed. Cron failures are dangerous because they are silent — no one notices until the damage is done.

Common Symptoms

  • Scheduled action shows "Last Execution" date weeks ago
  • Cron runs but does nothing (no errors, no results)
  • Error in logs: Exception during cron job
  • Cron stuck — running for hours and blocking others
  • "Number of calls" reached zero (cron exhausted)
  • Cron runs twice or creates duplicates

How Odoo Scheduled Actions Work

Odoo cron jobs run in the main Odoo process (or a dedicated cron worker). Key behaviors:

  • Each cron runs in its own database transaction
  • If a cron crashes, the transaction rolls back and the cron is marked as failed
  • After repeated failures, Odoo may stop retrying
  • Only one instance of a cron runs at a time (database lock)
  • Crons require --workers > 0 or --max-cron-threads > 0 in production

Causes and Fixes

1. Cron Worker Not Running

In multi-worker mode, Odoo dedicates specific workers to cron jobs. If max_cron_threads = 0, no cron will ever run.

Fix: Check odoo.conf:

# Must have at least 1 cron thread:
workers = 4
max_cron_threads = 1  # default is 2

# In development (single process):
# Crons run in the main thread, no config needed

2. Number of Calls Exhausted

Each scheduled action has a "Number of Calls" field. If set to a specific number (not -1), the cron stops after that many executions.

Fix: Go to Settings > Technical > Automation > Scheduled Actions. Find the action and set "Number of Calls" to -1 for unlimited execution.

3. Next Execution Date in the Past

If the "Next Execution Date" is far in the past and the cron was inactive for a while, Odoo may skip it or run it once and then calculate the next execution from the wrong base date.

Fix: Manually set "Next Execution Date" to now or a few minutes in the future, then save.

4. Python Code Error in Cron

The scheduled action calls a Python method that throws an exception, causing the cron to fail silently.

Check logs:

# Search for cron errors:
grep -i "cron\|scheduled" /var/log/odoo/odoo-server.log | tail -50

# Common errors:
Exception during cron job 'Send Emails':
KeyError: 'partner_id'
TypeError: 'NoneType' object is not iterable
psycopg2.errors.SerializationFailure: could not serialize access

Fix: Fix the underlying Python error. Common issues:

  • Code assumes records exist but they were deleted
  • Missing field on a record
  • Database constraint violation
  • External API call failing (timeout, auth error)

5. Cron Stuck / Long-Running

A cron that processes too many records in one batch can run for hours, blocking other crons.

Check:

# Find stuck cron processes:
SELECT pid, now() - query_start as duration, query 
FROM pg_stat_activity 
WHERE query LIKE '%ir_cron%' 
  AND state = 'active'
ORDER BY query_start;

Fix:

  • Kill the stuck process: SELECT pg_terminate_backend(pid);
  • Add batch processing to your cron code:
# BAD: processes all records at once
def _cron_process_all(self):
    records = self.env['model'].search([])
    for rec in records:
        rec.process()  # slow if thousands of records

# GOOD: process in batches
def _cron_process_batch(self):
    records = self.env['model'].search([], limit=100)
    for rec in records:
        rec.process()
    # Next cron execution will handle the next batch

6. Cron Running Twice / Duplicates

If the cron interval is too short and the processing time exceeds the interval, the next execution starts before the previous finishes. Odoo uses database locks to prevent this, but issues can occur.

Fix:

  • Increase the interval (e.g., from every 5 minutes to every 15)
  • Add a lock or flag to prevent concurrent execution:
def _cron_my_action(self):
    # Use advisory lock to prevent concurrent runs
    self.env.cr.execute(
        "SELECT pg_try_advisory_lock(%s)", [hash('my_cron_lock')]
    )
    if not self.env.cr.fetchone()[0]:
        return  # another instance is running
    try:
        # ... do work ...
        pass
    finally:
        self.env.cr.execute(
            "SELECT pg_advisory_unlock(%s)", [hash('my_cron_lock')]
        )

7. Cron Deactivated After Errors

After repeated failures, Odoo may deactivate the cron (set active = False).

Fix: Go to Settings > Technical > Scheduled Actions, remove the "Active" filter, find the deactivated cron, and reactivate it after fixing the underlying error.

8. Timezone Issues

Cron execution times are in UTC. If you expect a cron at 9 AM local time but your timezone is UTC+5, the cron runs at 4 AM local time.

Fix: Set the next execution date accounting for the UTC offset. Odoo stores all datetimes in UTC.

Debugging Steps

  1. Check the cron's status: Settings > Technical > Scheduled Actions
  2. Verify "Active", "Number of Calls", and "Next Execution Date"
  3. Check Odoo logs for errors during the cron's execution window
  4. Test the cron method manually in Odoo shell
  5. Check that max_cron_threads is not zero in odoo.conf
  6. Look for stuck database queries related to ir_cron