I recently ran into this issue in a production system, and after debugging it end-to-end, I realized that Odoo cron failures are usually silent by design unless we handle them explicitly.
This post walks through what actually goes wrong, why it happens, and how to fix it properly with code.
The Real Problem
Odoo cron jobs run in the background under the system user. When an exception occurs:
- Odoo may rollback the transaction
- The job execution stops
- The error is not surfaced in the UI
- The cron keeps rescheduling without doing any useful work This makes cron failures hard to detect and easy to ignore, especially in large systems.
Step 1: First Check If the Cron Is Even Running
Before touching code, verify the basics.
Go to:
Settings → Technical → Automation → Scheduled Actions
Check:
- Is the cron Active?
- Is the Next Execution Date updating?
- Is numbercall exhausted?
If the next execution date never changes, the cron is not running at all.
Step 2: Enable Cron-Specific Logging (Often Missed)
By default, cron logs are easy to miss.
Update your odoo.conf:
log_level = info
log_handler = :INFO,odoo.addons.base.models.ir_cron:DEBUG
Restart the server.
Now cron activity and failures will actually appear in logs.
Step 3: Why Crons Fail Silently (The Core Reason)
Most cron methods are written like normal business logic.
That’s the mistake.
Typical problematic code
def run_daily_job(self):
orders = self.env['sale.order'].search([])
for order in orders:
order.process()
If process() fails even once:
- The entire cron crashes
- No error is visible unless logging is perfect
Step 4: Fix It With Proper Error Handling (Mandatory)
Always wrap cron logic in try–except.
import logging
from odoo import models, api
_logger = logging.getLogger(__name__)
class SaleCron(models.Model):
_inherit = 'sale.order'
@api.model
def run_daily_job(self):
try:
orders = self.search([])
for order in orders:
order.process()
_logger.info("Sale cron executed successfully")
except Exception as e:
_logger.error(
"Sale cron failed: %s", str(e), exc_info=True
)
This single change:
- Prevents silent failures
- Preserves stack traces
- Makes debugging possible
Step 5: Prevent One Bad Record From Killing the Cron
A very common issue is one corrupted record stopping everything.
Use database savepoints.
@api.model
def run_daily_job(self):
orders = self.search([])
for order in orders:
try:
with self.env.cr.savepoint():
order.process()
except Exception as e:
_logger.error(
"Failed for order %s: %s", order.name, str(e)
)
Now:
- One record fails → logged
- Remaining records still process
Step 6: Avoid User-Dependent Code in Crons
Crons do not run as the logged-in user.
This often breaks code like:
self.env.user.email
Instead, explicitly fetch a known user:
admin = self.env.ref('base.user_admin')
email = admin.partner_id.email
Never assume UI user context in background jobs.
Step 7: Make Sure the Cron Doesn’t Auto-Stop
If numbercall reaches 0, the cron never runs again.
Correct XML definition:
<record id="ir_cron_daily_sale_job" model="ir.cron">
<field name="name">Daily Sale Processing</field>
<field name="model_id" ref="sale.model_sale_order"/>
<field name="state">code</field>
<field name="code">model.run_daily_job()</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="active">True</field>
</record>
-1 ensures the cron runs forever.
Step 8: Add Email Alerts for Production Safety
Logs are not enough in production.
def notify_admin(self, error):
mail = self.env['mail.mail'].create({
'subject': 'Odoo Cron Job Failed',
'body_html': f'<p>{error}</p>',
'email_to': 'admin@company.com',
})
mail.send()
Trigger this inside the except block.
Step 9: Always Test Crons Manually
Before trusting a cron:
odoo shell -d your_database
env['sale.order'].run_daily_job()
If it fails here, it will fail in production.
Step 10: Watch for Database Locks (Hidden Cron Killers)
Long-running queries and locks can block cron execution.
SELECT * FROM pg_stat_activity WHERE state = 'active';
Slow ORM queries often look harmless but can freeze background jobs.
Key Takeaways
Odoo cron jobs don’t fail loudly by default.
They fail quietly, repeatedly, and dangerously.
The fix is not complex, but it must be intentional:
- Always handle exceptions
- Log aggressively
- Use savepoints
- Avoid user assumptions
- Monitor cron health
Once these patterns are in place, cron jobs become predictable, observable, and safe for production.
Top comments (0)