Odoo Insights

Odoo 15 to 16 migration breaks custom reports

Custom reports that worked in Odoo 15 fail silently after upgrading to Odoo 16, causing daily operations to stall. This guide shows where to look, what to test, and how to restore reports without rewriting code.

2026-05-06 · 11 minutes

The silent failure that stops daily operations

When you move from Odoo 15 to Odoo 16, custom PDF reports that once printed invoices, delivery notes, or quotations stop working. The symptoms are subtle: the report preview shows blank pages, the print button does nothing, or you receive an error like QWebException: Template not found. Operations staff notice the issue only when customers complain about missing documents.

This failure pattern is common when custom reports rely on code or templates that changed between versions. Odoo 16 introduced stricter template validation and deprecated some older QWeb features used in older custom reports. The risk is higher if your implementation includes custom addons or integrations that modify report behavior.

Ignoring this issue delays order fulfillment, slows down accounting, and creates compliance gaps. The fix is usually straightforward once you know where to look.

Why custom reports break after migration

Odoo 16 tightened the rules for report templates written in QWeb, the templating system used to generate PDFs. Two changes cause most failures:

  • Template inheritance changes: Older reports used t-extend or t-set in ways that are no longer allowed. Odoo 16 enforces stricter inheritance rules, so if your custom report extends a core template incorrectly, it fails to render.
  • Deprecated QWeb tags: Tags like <t t-foreach="..." t-as="..."> required explicit variable declarations in older versions. In Odoo 16, missing declarations trigger validation errors during report generation.

Additionally, if your reports use custom Python code in the @api.view decorator or rely on old report actions defined in XML, those may also break due to API changes in the new version.

This problem is most common in implementations that customized reports through /odoo-implementation.html or developed custom addons for reporting, as seen in /odoo-custom-addons.html. Integrations that call report generation via API are less affected unless they use deprecated endpoints.

Quick checks before you dig deeper

Start with the obvious symptoms

Before running complex diagnostics, confirm the issue is with the report itself and not with the environment:

  • Check the error log: Open Odoo server logs and search for QWebException, Template not found, or Invalid template. These messages point directly to the failing template.
  • Test with a core report: Try printing a standard report like Sales Order. If it works, the issue is isolated to your custom reports.
  • Verify the report action: Go to Settings > Technical > Reports and open your custom report. Check if the Report Action field points to the correct template name. A typo here causes silent failures.

If none of these show the problem, move to the next section.

Check for template inheritance errors

A common failure pattern is a custom report that extends a core template but uses incorrect syntax. For example, a report extending report_invoice_document might use:

<template id="custom_invoice_report" inherit_id="report_invoice_document">
    <xpath expr="//div[@class='page']" position="inside">
        <p>Custom footer</p>
    </xpath>
</template>

In Odoo 16, this fails if the inherit_id is misspelled or if the XPath expression is invalid. The error message will read: ValueError: Invalid XPath expression.

Compare your template’s inherit_id with the one in Odoo 16’s source code. You can find it in the addons/web/static/src/xml folder of the Odoo 16 source tree.

How to fix custom report templates in 4 steps

Use this checklist to restore your custom reports after migration. The steps assume you have access to the server file system and basic XML editing skills.

Step 1 – Identify the failing report

  • Go to Settings > Technical > Reports.
  • Filter by External ID or Name to find your custom report.
  • Note the Template field value (e.g., custom_invoice_report).

Step 2 – Locate the template file

Most custom reports are defined in XML files under your_addon_name/views/. Open the file that contains the report template. If you’re unsure, search for <template with the same id as your report’s Template field.

Step 3 – Validate template syntax

Check for these common Odoo 16 incompatibilities:

Old pattern Odoo 16 replacement How to fix
<t t-foreach="..." t-as="item"> <t t-foreach="..." t-as="item"> (unchanged, but must declare variables) Ensure item is defined in the report context. Add <t t-set="item" t-value="..." /> if missing.
<t t-extend="report_invoice_document"> <template id="custom_invoice_report" inherit_id="report_invoice_document"> Replace t-extend with inherit_id. Fix any XPath errors.
<field name="arch"><![CDATA[...]]></field> <field name="arch" type="xml">...</field> Change CDATA to type="xml" in the field definition.

After editing, save the file and restart the Odoo server to reload templates.

Step 4 – Test the report

  • Go to a Sales Order or Invoice.
  • Open the Action menu and select your report.
  • If the preview loads, the fix worked.
  • If you still see a blank page or error, check the server log again for new clues.

When to seek help instead of fixing yourself

Not all report failures are template-based. If you see any of these symptoms, stop and escalate:

  • Database corruption: Errors like psycopg2.OperationalError: could not connect to server or duplicate key value violates unique constraint indicate data damage.
  • Module crashes on startup: If Odoo fails to start after your changes with ImportError or AttributeError, your XML may be invalid or your code has syntax errors.
  • Reports vanish from UI: If the report disappears from the Action menu after migration, it may be due to a missing module dependency or a failed upgrade.
  • Data loss risk: If you’re unsure about data integrity or need to back up before changes, pause and contact your implementation partner.

For custom addons, check the /odoo-custom-addons.html documentation for version-specific changes. If your addon integrates with external systems via /odoo-integration.html, test the integration after fixing the report to avoid downstream failures.

If you’re on a paid plan, review the /odoo-pricing.html support tiers. Basic support covers report issues, while advanced support handles data recovery.

Preventing report failures in future upgrades

Use this preventive checklist before any Odoo upgrade to avoid report issues:

Before upgrade

  • Audit custom reports: List all custom reports and their dependencies. Note which templates extend core reports or use custom logic.
  • Back up templates: Export all custom report XML files and store them in version control.
  • Test in staging: Run the upgrade in a staging environment first. Use sample data to generate reports and compare outputs between versions.

After upgrade

  • Validate all reports: Print a sample of each report type (invoice, PO, SO) and verify content and layout.
  • Check integrations: If reports are pushed to external systems via /odoo-integration.html, rerun integration tests.
  • Monitor logs: Watch for QWebException or Template not found for 48 hours after go-live.

Document the changes you make to templates so future upgrades are faster. Keep a changelog in your custom addon’s CHANGELOG.md file with version numbers and fixes applied.

Summary: What to do today

If your custom reports stopped working after upgrading from Odoo 15 to 16:

  1. Check the error log for QWebException or Template not found.
  2. Open the report template XML in your custom addon and fix inheritance and QWeb syntax as shown above.
  3. Restart Odoo and test the report in the UI.
  4. If it fails again, escalate using the triggers in the "When to seek help" section.

This issue is usually resolved in under an hour if you focus on template syntax and inheritance. Delaying the fix risks operational delays and compliance gaps. Start with the diagnostic checklist in the "Quick checks" section and work through the steps methodically.

Practical Next Step

Check your reports in 5 minutes using the diagnostic checklist

Discuss Your Odoo Scope

FAQ

What is a QWebException and how do I read it?

A QWebException is an error thrown by Odoo when a report template fails to render. It usually includes a line like: QWebException: Template 'custom_invoice_report' not found. This means Odoo cannot locate the template XML file or the template ID is misspelled. Check the Template field in the report definition and ensure the XML file is in the correct module and loaded during startup.

I fixed the template but the report still shows a blank page. What’s next?

If the template loads but the page is blank, the issue is likely with the report context or data. First, verify that the report action passes the correct context. Add context="{'lang': 'en_US'}" to the report action if language issues are suspected. If the problem persists, check the server log for warnings about missing variables or database records. Restarting Odoo with --log-level=debug can reveal hidden context errors.

Can I migrate reports without touching code or XML?

No. Odoo 16 enforces stricter template rules, so custom reports that relied on deprecated patterns must be updated. You can avoid rewriting reports by using standard Odoo 16 report templates and customizing only via the UI (e.g., adding fields via Studio). For advanced layouts, minimal XML changes are required. Review the Odoo 16 migration guide for report changes before proceeding.

How do I know if my custom addon is causing the report to fail?

Disable your custom addon temporarily by renaming its folder in the addons directory or using --without-demo=all during startup. If the report works after disabling the addon, the issue is in the addon. Re-enable it and review the addon’s XML files for report templates and dependencies. Check for duplicate template IDs or incorrect module inheritance.

What is the risk of running a half-fixed report in production?

The risk is operational: missing or incomplete documents can delay orders, invoices, or shipments. From a technical standpoint, a half-fixed report may cause Odoo to crash during high-load periods or corrupt data if it writes partial records. Always test fixes in a staging environment before applying them to production. If you must deploy a partial fix, monitor the report queue and logs closely for 24 hours.