From 15ac819748f12c728a9f2c6edc1294b9caba65ab Mon Sep 17 00:00:00 2001 From: Florian da Costa Date: Thu, 10 Sep 2026 23:57:25 +0200 Subject: [PATCH 1/4] base_exception: no second cursor when installing modules detect_exceptions() stores the exceptions in a separate transaction (a new cursor/connection) so that they are kept when the ongoing transaction is rolled back. When the registry is not ready yet (module installation or update), the ongoing transaction holds exclusive locks on the tables it has just modified (ALTER TABLE ...). The call to _must_raise_exception_after_detection() made inside that new transaction then reads the model records, and the query waits for those locks forever: the only thread able to release them is the one waiting for them. This happens as soon as demo data confirming a sale order is loaded during a database initialisation with sale_exception installed (e.g. the sale_stock demo data), making the whole database initialisation hang. Use the current cursor in that case, as already done when running tests. --- base_exception/models/base_exception_method.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/base_exception/models/base_exception_method.py b/base_exception/models/base_exception_method.py index 9a34abf43be..41b7318a097 100644 --- a/base_exception/models/base_exception_method.py +++ b/base_exception/models/base_exception_method.py @@ -90,9 +90,16 @@ def detect_exceptions(self): # table # and the "to add" part generates one INSERT (with unnest) per rule. raise_exception = False - test_mode = config["test_enable"] and not self.env.context.get( - "test_base_exception" - ) + # The registry is not ready yet when modules are being installed or + # updated: the ongoing transaction holds exclusive locks on the tables + # it has just modified (ALTER TABLE ...), so any query made through a + # second connection would wait forever on those locks (and the locks + # cannot be released, as the current thread is the one waiting). + # Use the current cursor in that case, as already done when running + # tests. + test_mode = ( + config["test_enable"] or not self.env.registry.ready + ) and not self.env.context.get("test_base_exception") # Write exceptions in a new transaction to be committed so that we can # rollback the ongoing one while keeping the exceptions stored with self.env.registry.cursor() as new_cr: From ab30c01d2f4314640cb5534666a9cb6ffec15da2 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 11 Sep 2026 09:42:02 +0000 Subject: [PATCH 2/4] [BOT] post-merge updates --- README.md | 2 +- base_exception/README.rst | 2 +- base_exception/__manifest__.py | 2 +- base_exception/static/description/index.html | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0d259c0b0ae..e16bdda490e 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ addon | version | maintainers | summary [autovacuum_message_attachment](autovacuum_message_attachment/) | 18.0.1.0.1 | florian-dacosta | Automatically delete old mail messages and attachments [base_conditional_image](base_conditional_image/) | 18.0.1.0.0 | | This module extends the functionality to support conditional images [base_cron_exclusion](base_cron_exclusion/) | 18.0.1.0.1 | LoisRForgeFlow ChrisOForgeFlow | Allow you to select scheduled actions that should not run simultaneously. -[base_exception](base_exception/) | 18.0.1.1.2 | hparfr sebastienbeau | This module provide an abstract model to manage customizable exceptions to be applied on different models (sale order, invoice, ...) +[base_exception](base_exception/) | 18.0.1.2.0 | hparfr sebastienbeau | This module provide an abstract model to manage customizable exceptions to be applied on different models (sale order, invoice, ...) [base_fontawesome](base_fontawesome/) | 18.0.2.0.0 | | Up to date Fontawesome resources. [base_fontawesome_web_editor](base_fontawesome_web_editor/) | 18.0.1.0.0 | | Integration between base_fontawesome and web_editor for FontAwesome >= 6.7.2 support. [base_force_record_noupdate](base_force_record_noupdate/) | 18.0.1.0.0 | | Manually force noupdate=True on models diff --git a/base_exception/README.rst b/base_exception/README.rst index 0987d5141ee..7e6d4b2c727 100644 --- a/base_exception/README.rst +++ b/base_exception/README.rst @@ -11,7 +11,7 @@ Exception Rule !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:bf43c9d6f42f7195b276ef11de74a1a145886fbc5928d55fe35e8763fb5fd866 + !! source digest: sha256:8ed6b8146a4f13987bd621181e7ab8e77db1cf2cc626387b2fe3ffa02cfa64d0 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Mature-brightgreen.png diff --git a/base_exception/__manifest__.py b/base_exception/__manifest__.py index 54cb35736e0..5c3f6bbfc05 100644 --- a/base_exception/__manifest__.py +++ b/base_exception/__manifest__.py @@ -5,7 +5,7 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). { "name": "Exception Rule", - "version": "18.0.1.1.2", + "version": "18.0.1.2.0", "development_status": "Mature", "category": "Generic Modules", "summary": """ diff --git a/base_exception/static/description/index.html b/base_exception/static/description/index.html index 54c84e8ede8..2972df525cd 100644 --- a/base_exception/static/description/index.html +++ b/base_exception/static/description/index.html @@ -372,7 +372,7 @@

Exception Rule

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:bf43c9d6f42f7195b276ef11de74a1a145886fbc5928d55fe35e8763fb5fd866 +!! source digest: sha256:8ed6b8146a4f13987bd621181e7ab8e77db1cf2cc626387b2fe3ffa02cfa64d0 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Mature License: AGPL-3 OCA/server-tools Translate me on Weblate Try me on Runboat

This module provide an abstract model to manage customizable exceptions From 29eb9822b436da4c13f735743c09fc79d853b3d5 Mon Sep 17 00:00:00 2001 From: sergio-teruel Date: Mon, 14 Sep 2026 13:56:00 +0200 Subject: [PATCH 3/4] [FIX] base_exception: MissingError on records created in the ongoing transaction detect_exceptions() opens a second, genuinely independent DB connection (self.env.registry.cursor()) to write exception flags so they survive a rollback of the ongoing transaction. It then re-derived main_records through that same second connection (self.with_env(new_env)), which crashes with MissingError whenever _get_main_records() has to read a field on records created earlier in the SAME ongoing transaction and not yet committed (e.g. sale.order.line._get_main_records() reading order_id right after the line was created by write()). Typical trigger: adding a product to an already confirmed sale order. sale_exception.write() re-runs detect_exceptions() on order_line whenever order_line changes on a confirmed order; the new line only exists in the ongoing transaction, so the second connection can't see it yet. Resolve main_records once through the original (main) cursor, which does see the just-created records, and only rebind that already-resolved recordset to the new environment for the exception_ids check. --- .../models/base_exception_method.py | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/base_exception/models/base_exception_method.py b/base_exception/models/base_exception_method.py index 41b7318a097..7b934d634a8 100644 --- a/base_exception/models/base_exception_method.py +++ b/base_exception/models/base_exception_method.py @@ -100,6 +100,10 @@ def detect_exceptions(self): test_mode = ( config["test_enable"] or not self.env.registry.ready ) and not self.env.context.get("test_base_exception") + # Resolve the main records (e.g. the sale order behind a sale order + # line) using the current cursor, which sees records created earlier + # in this same transaction even before they are committed. + main_records = self._get_main_records() # Write exceptions in a new transaction to be committed so that we can # rollback the ongoing one while keeping the exceptions stored with self.env.registry.cursor() as new_cr: @@ -118,9 +122,21 @@ def detect_exceptions(self): ) # In case we have new exception, or exceptions that were not ignored yet, or # blocking exceptions, we need to raise an exception to rollback the - # ongoing transaction - self_new_env = self.with_env(new_env) - if rules_to_add or self_new_env._must_raise_exception_after_detection(): + # ongoing transaction. + # Re-derive main_records through new_env rather than re-running + # self.with_env(new_env)._get_main_records(): when self are + # records just created earlier in the ongoing (not yet committed) + # transaction (e.g. a line added while editing a confirmed sale + # order), new_cr is a genuinely separate DB connection that + # cannot see them yet, and _get_main_records() traversal + # (e.g. sale.order.line -> order_id) would raise MissingError. + # main_records itself was already resolved above through the + # current cursor, so only rebinding it to new_env is needed here. + main_records_new_env = main_records.with_env(new_env) + if ( + rules_to_add + or main_records_new_env._must_raise_exception_after_detection() + ): raise_exception = True if raise_exception: raise BaseExceptionError( From 60cd54e5ee8f6538a14266af8a7b3d1da0af08ec Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 15 Sep 2026 06:45:25 +0000 Subject: [PATCH 4/4] [BOT] post-merge updates --- README.md | 2 +- base_exception/README.rst | 2 +- base_exception/__manifest__.py | 2 +- base_exception/static/description/index.html | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e16bdda490e..186a69b451e 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ addon | version | maintainers | summary [autovacuum_message_attachment](autovacuum_message_attachment/) | 18.0.1.0.1 | florian-dacosta | Automatically delete old mail messages and attachments [base_conditional_image](base_conditional_image/) | 18.0.1.0.0 | | This module extends the functionality to support conditional images [base_cron_exclusion](base_cron_exclusion/) | 18.0.1.0.1 | LoisRForgeFlow ChrisOForgeFlow | Allow you to select scheduled actions that should not run simultaneously. -[base_exception](base_exception/) | 18.0.1.2.0 | hparfr sebastienbeau | This module provide an abstract model to manage customizable exceptions to be applied on different models (sale order, invoice, ...) +[base_exception](base_exception/) | 18.0.1.3.0 | hparfr sebastienbeau | This module provide an abstract model to manage customizable exceptions to be applied on different models (sale order, invoice, ...) [base_fontawesome](base_fontawesome/) | 18.0.2.0.0 | | Up to date Fontawesome resources. [base_fontawesome_web_editor](base_fontawesome_web_editor/) | 18.0.1.0.0 | | Integration between base_fontawesome and web_editor for FontAwesome >= 6.7.2 support. [base_force_record_noupdate](base_force_record_noupdate/) | 18.0.1.0.0 | | Manually force noupdate=True on models diff --git a/base_exception/README.rst b/base_exception/README.rst index 7e6d4b2c727..46de37e496a 100644 --- a/base_exception/README.rst +++ b/base_exception/README.rst @@ -11,7 +11,7 @@ Exception Rule !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:8ed6b8146a4f13987bd621181e7ab8e77db1cf2cc626387b2fe3ffa02cfa64d0 + !! source digest: sha256:97057fe274486f1fe87790f8eb4aa862ccd79dcd71321bd0d5c621ab90545b75 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Mature-brightgreen.png diff --git a/base_exception/__manifest__.py b/base_exception/__manifest__.py index 5c3f6bbfc05..e87075db29f 100644 --- a/base_exception/__manifest__.py +++ b/base_exception/__manifest__.py @@ -5,7 +5,7 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). { "name": "Exception Rule", - "version": "18.0.1.2.0", + "version": "18.0.1.3.0", "development_status": "Mature", "category": "Generic Modules", "summary": """ diff --git a/base_exception/static/description/index.html b/base_exception/static/description/index.html index 2972df525cd..be728c51fcc 100644 --- a/base_exception/static/description/index.html +++ b/base_exception/static/description/index.html @@ -372,7 +372,7 @@

Exception Rule

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:8ed6b8146a4f13987bd621181e7ab8e77db1cf2cc626387b2fe3ffa02cfa64d0 +!! source digest: sha256:97057fe274486f1fe87790f8eb4aa862ccd79dcd71321bd0d5c621ab90545b75 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Mature License: AGPL-3 OCA/server-tools Translate me on Weblate Try me on Runboat

This module provide an abstract model to manage customizable exceptions