In Odoo, exceptions are used to stop execution and show meaningful messages to users or developers when something goes wrong. Since you’re already working deeply with Odoo backend, security, and business logic, this explanation will be practical, real-world, and example-driven rather than just theoretical.
Odoo exceptions are mainly located in:
from odoo.exceptions import (
AccessError,
UserError,
ValidationError,
MissingError,
RedirectWarning,
Warning, # legacy
)1.AccessError – Permission / Security Violation
Used when a user does not have permission to perform an action.
Typical Use Cases
- User tries to read/write/create/delete records without access rights
- Custom security checks beyond record rules
- Restricting actions based on user groups
from odoo.exceptions import AccessError
def action_approve(self):
if not self.env.user.has_group('base.group_system'):
raise AccessError("Only administrators can approve this record.")- Triggers HTTP 403
- Automatically translated
- Should be used for security-related checks
2.UserError – Business Logic Error (Most Common)
Show a friendly error message to the user when business logic fails.
Typical Use Cases
- Required business condition not met
- Invalid workflow state
- Missing required configuration
from odoo.exceptions import UserError
def action_confirm(self):
if not self.partner_id:
raise UserError("Please select a customer before confirming.")Real-World Use Case
- Confirming a Sale Order without products
- Posting a Journal Entry without accounts
Submitting a form with missing required data
3.ValidationError – Data Validation Error
Used when field-level or model-level validation fails.
Typical Use Cases
- Date validation
- Numeric constraints
- Email/phone format checks
from odoo.exceptions import ValidationError
from odoo import api, models
class Employee(models.Model):
_inherit = 'hr.employee'
@api.constrains('age')
def _check_age(self):
for rec in self:
if rec.age < 18:
raise ValidationError("Employee must be at least 18 years old.")Real-World Use Case
- Future date not allowed
- Salary must be positive
- End date cannot be before start date
- Automatically blocks
create()andwrite() - Best for data integrity
- Appears like a form validation error
4.MissingError – Record Not Found / Deleted
Raised when trying to access a record that no longer exists.
from odoo.exceptions import MissingError
def action_view(self):
if not self.exists():
raise MissingError("This record has been deleted.")Real-World Use Case
- User opens a record in two tabs
- Record is deleted in another session
- Clicks on a broken action
- Rarely raised manually
- Often raised internally by Odoo ORM
5.RedirectWarning – Force User to Open Another View
Shows a warning with a button redirecting to another action.
from odoo.exceptions import RedirectWarning
def action_post(self):
if not self.journal_id.default_account_id:
action = self.env.ref('account.action_account_journal_form').id
raise RedirectWarning(
"Please configure default account for this journal.",
action,
"Open Journal"
)Real-World Use Case
- Missing configuration
- Redirect user to settings page
- Force setup before continuing
- Very user-friendly
- Rare but powerful
- Often used in Accounting modules
6.Python Standard Exceptions (Used in Odoo)
You’ll also encounter:
ValueError
TypeError
KeyError
ExceptionDon’t show raw Python exceptions to users
Convert them to UserError or ValidationError
Example:
try:
result = int(self.value)
except ValueError:
raise UserError("Please enter a valid number.")
Exception Selection Cheat Sheet
| Scenario | Use |
|---|---|
| Permission issue | AccessError |
| Business rule failed | UserError |
| Field/model validation | ValidationError |
| Record missing | MissingError |
| Need redirect | RedirectWarning |
| Old code | Warning ? |
Best Practices (Very Important)
-Use specific exceptions, not generic
-Messages should be user-friendly
-Never expose technical details
-Use ValidationError inside constraints
-Use AccessError only for security
Real Example (Odoo-Style Perfect Code)
def action_submit(self):
if not self.env.user.has_group('hr.group_hr_manager'):
raise AccessError("You are not allowed to submit this request.")
if not self.document_id:
raise UserError("Please attach the required document.")
if self.date > fields.Date.today():
raise ValidationError("Date cannot be in the future.")
