[18.0][FIX] auditlog: prevent recursion on re-entrant write from compute/inverse - #3658
Open
midhlaj-nk wants to merge 1 commit into
Open
[18.0][FIX] auditlog: prevent recursion on re-entrant write from compute/inverse#3658midhlaj-nk wants to merge 1 commit into
midhlaj-nk wants to merge 1 commit into
Conversation
…ute/inverse write_full and write_fast set auditlog_disabled on a rebound local self via with_context(), but compute/inverse methods triggered by the original write (e.g. AccountAccount._compute_code / _inverse_code) call write() on the model class using their own self, which does not carry the modified context. This causes write_full/write_fast to re-enter indefinitely, hitting Python's recursion limit. Add an early-exit guard at the top of both closures: if auditlog_disabled is already in context, delegate immediately to the origin write and return. Closes OCA#3512
StefanRijnhart
requested changes
Jul 26, 2026
StefanRijnhart
left a comment
Member
There was a problem hiding this comment.
Thanks for having a go at this. The added test fails, but also I fear this solution might undermine the situation in which the write of a tracked model triggers a write on another tracked model. The changes on the latter model might not be recorded due to the application of the context key. Furthermore, the semantics of this particular context key, which was somewhat unfortunately named I believe was to disable read logging in particular. All of this has been digested on #3646 which I think is in a pretty good shape now. Maybe you can test and review that version, and maybe try to cherry-pick its solution for 18.0?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When an auditlog rule is active for a model that has compute or inverse methods that trigger
write()on the same model (e.g.account.accountwith_compute_code/_inverse_code), aRecursionError: maximum recursion depth exceededis raised.Root cause:
write_fullandwrite_fastsetauditlog_disabled=Trueviaself = self.with_context(auditlog_disabled=True), which only modifies the localself. Compute/inverse methods triggered by the original write callwrite()on the model class using their ownself, which does not carry the modified context. This causes the patchedwrite_full/write_fastto re-enter indefinitely.Closes #3512
Solution
Add an early-exit guard as the first statement of both
write_fullandwrite_fast:When a re-entrant write call arrives (already carrying
auditlog_disabled=True), the closure immediately delegates to the origin and returns — no logging, no further recursion.Tests
Added
TestAuditlogRecursionwith three test cases:test_no_recursion_write_full_reentrant: company write syncs address to child partner (both audited, full log) — verifies no RecursionErrortest_no_recursion_write_fast_reentrant: same scenario with fast logtest_write_full_guard_skips_logging: write withauditlog_disabled=Truein context produces no log entry — verifies guard path is taken