The Error
odoo.exceptions.MissingError: Record does not exist or has been deleted.
(Record: sale.order(42,), User: 2)
# Or in the UI:
"The record you are trying to access has been deleted."What It Means
Code is trying to access a record (by its ID) that no longer exists in the database. The record was either deleted by another user, a cron job, a cascade delete, or the ID reference is stale.
Cause 1: Record Deleted by Another User
# User A opens sale order #42
# User B deletes sale order #42
# User A clicks a button → MissingError
# Fix: handle gracefully in code
record = self.env['sale.order'].browse(42)
if not record.exists():
raise UserError("This record has been deleted.")
record.action_confirm()Cause 2: Cascade Delete
# Deleting a parent record cascades to children:
# Delete partner → deletes their sale orders → deletes order lines
# If code holds a reference to an order line → MissingError
# Check ondelete behavior:
partner_id = fields.Many2one('res.partner', ondelete='cascade')
# cascade: delete child when parent is deleted
# set null: set to NULL when parent is deleted
# restrict: prevent parent deletion if children exist
# Fix: use ondelete='restrict' to prevent accidental cascades
# Or check .exists() before accessing related recordsCause 3: Cron Job Cleanup
# A cron deletes old records while a user is viewing them:
# - Cron cleans up old draft quotations
# - User has an old draft open in their browser
# - User clicks Save → MissingError
# Fix: cron should check for active sessions
# Or: soft-delete (active=False) instead of hard deleteCause 4: Stale Browser Tab
# User has Odoo open in a browser tab for hours
# Records were deleted in that time
# User tries to interact → MissingError
# Fix (frontend): catch the error and show a friendly message
# Fix (backend): always check .exists() before operationsCause 5: Orphaned Many2many References
# Many2many relation table has IDs of deleted records:
SELECT * FROM model_a_model_b_rel
WHERE model_a_id NOT IN (SELECT id FROM model_a)
OR model_b_id NOT IN (SELECT id FROM model_b);
# Fix: clean up orphaned references
DELETE FROM model_a_model_b_rel
WHERE model_a_id NOT IN (SELECT id FROM model_a)
OR model_b_id NOT IN (SELECT id FROM model_b);Cause 6: Database Restore Mismatch
# Restored an older backup but filestore is newer
# Or: restored database but ir_attachment references non-existent records
# Fix: restore both database AND filestore from the same backup pointPrevention Patterns
# Pattern 1: Always check exists()
def action_process(self):
for record in self:
if not record.exists():
continue
record._do_processing()
# Pattern 2: Use filtered()
records = self.env['sale.order'].browse(ids)
existing = records.filtered(lambda r: r.exists())
# Or simply:
existing = records.exists() # Returns recordset of existing records
# Pattern 3: Catch MissingError
from odoo.exceptions import MissingError
try:
record.action_confirm()
except MissingError:
raise UserError("This record was deleted. Please refresh.")
# Pattern 4: Use sudo_browse_or_404 pattern
record = self.env['sale.order'].browse(record_id)
if not record.exists():
return request.not_found() # In controllersDiagnosis Steps
- Note the model and ID from the error (e.g., sale.order(42))
- Check if the record exists:
SELECT id FROM sale_order WHERE id = 42; - If it doesn't exist, check who deleted it: audit log or
git blameon cron code - If it exists, check if there's a permissions issue instead
- Check for cascade deletes on related models
DeployMonkey
DeployMonkey's AI agent catches MissingError patterns and identifies the root cause — whether it is a cascade delete, cron cleanup, or orphaned reference. It suggests the fix and helps prevent recurrence.