Odoo development involves debugging, which is an important part. A Python model, an OWL component, an RPC call, or a record rule that prevents actions can be the source of problems with Odoo 19. Additionally, Odoo 19 made enough internal changes to the HTTP layer, OWL design, and frontend bundling that some of the previous debugging techniques are no longer effective.
This blog discusses real debugging methods for Odoo 19, including backend faults, frontend rendering problems, ORM behavior, and access permissions, using time-saving tools and procedures rather than only theoretical ones.
1. Understand what Odoo 19 actually sends back:
Odoo continues to return HTTP 200 and embed the actual exception within the JSON response body for the majority of JSON-RPC application problems. That is intentional rather than a bug. The JSON-RPC error object, which is located inside the response body, contains the actual error. People are constantly caught off guard by this.
This is how an actual Odoo 19 error response appears:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": 200,
"message": "Odoo Server Error",
"data": {
"name": "odoo.exceptions.ValidationError",
"debug": "Traceback (most recent call last):\n ...",
"message": "The field 'email' is required.",
"arguments": ["The field 'email' is required."]
}
}
}Note that there's no exception_type field here. That field existed in older versions but was removed during the HTTP refactor. If you're parsing it in your code, it'll silently return undefined. Use data.name instead — it's stable across versions.
Tip: Always check response.error?.data?.name in your client code. String-match against the exception class name rather than any code or type field.
2. Enable server-side debug logging:
Browser DevTools only shows you the response. To see what's happening inside Odoo, you need the server logs. In Odoo 19, add this to your config file:
[options]
log_level = debug
log_handler = :DEBUG
Or pass it at startup:
python odoo-bin --log-level=debug --log-handler=:DEBUG -c odoo.conf
For workflow-specific debugging, you want more targeted logging to avoid noise. Target your module directly:
--log-handler=odoo.addons.your_module:DEBUG
This keeps the logs readable when you have a large instance running multiple modules.
3. Use Python's debugger inside Odoo methods:
Sometimes logs aren't enough. Drop a breakpoint directly into your model method:
def action_confirm(self):
import pdb; pdb.set_trace()
# or in Python 3.7+
breakpoint()
return super().action_confirm()
Warning: Never leave breakpoints in production. If Odoo freezes unexpectedly in dev, check whether a pdb call is blocking the process — it won't show an error, it just hangs.
4. Intercept JSON-RPC calls from the frontend:
The call passes through /web/dataset/call_kw if your workflow is triggered by a button or an OWL component. Open DevTools > Network in the browser, search for those requests, and filter by XHR. Both the response and the payload are JSON that may be read.
You can temporarily modify the RPC service in your JS to report every outgoing RPC call while debugging:
In your part or service, momentarily:
import { useService } from "@web/core/utils/hooks";
setup() {
this.rpc = useService("rpc");
const originalRpc = this.rpc;
this.rpc = async (...args) => {
console.log("[RPC Call]", args);
const result = await originalRpc(...args);
console.log("[RPC Result]", result);
return result;
};
}Remove this before committing. It's a debugging tool, not a logging strategy.
5. Handle workflow errors properly in custom modules:
If you're writing a custom workflow and want Odoo to surface errors cleanly to the UI, use the right exception classes. Odoo 19 maps them to the correct HTTP behavior:
from odoo.exceptions import UserError, ValidationError, AccessError
# For business logic errors (shows as a user-visible warning dialog)
raise UserError("You can't confirm an order without a customer.")
# For field validation failures
raise ValidationError("The delivery date cannot be in the past.")
# For permission issues
raise AccessError("You don't have rights to approve this record.")
# Record deleted mid-transaction
raise MissingError("Record missing.")
Don't use generic Python exceptions like ValueError or Exception — Odoo catches those too, but the client-side error handling is messier and the debug output is less useful.
6. The workflow state machine isn't always obvious:
Server actions, scheduled actions, and field-triggered automations are examples of Odoo's automated processes that operate within their own transaction context. The entire transaction rolls back if something goes wrong in the middle of the operation, although there isn't always a noticeable fault.
Examine the ir.logging table directly:
SELECT name, level, message, create_date
FROM ir_logging
WHERE level IN ('ERROR', 'WARNING')
ORDER BY create_date DESC
LIMIT 50;
This is often faster than tailing log files, especially when you don't have direct server access.
To read more about Overview of Debugging in Odoo 19, refer to our blog Overview of Debugging in Odoo 19.