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 > 0or--max-cron-threads > 0in 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 needed2. 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 accessFix: 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 batch6. 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
- Check the cron's status: Settings > Technical > Scheduled Actions
- Verify "Active", "Number of Calls", and "Next Execution Date"
- Check Odoo logs for errors during the cron's execution window
- Test the cron method manually in Odoo shell
- Check that
max_cron_threadsis not zero in odoo.conf - Look for stuck database queries related to ir_cron