Skip to content

[19.0][FIX] queue_job: repair default_env in runjob - #947

Closed
Ricardoalso wants to merge 1 commit into
OCA:19.0from
camptocamp:fix-runjob-default-env-auth-none
Closed

[19.0][FIX] queue_job: repair default_env in runjob#947
Ricardoalso wants to merge 1 commit into
OCA:19.0from
camptocamp:fix-runjob-default-env-auth-none

Conversation

@Ricardoalso

Copy link
Copy Markdown

IrHttp._auth_method_none() forces transaction.default_env to an environment with uid=None for every auth="none" route, including /queue_job/runjob. Any env.user access during a savepoint-triggered flush inside a job (e.g. a recompute/constrain calling has_group()) then raises "Expected singleton: res.users()", since browse(None) is an empty recordset.

Repair default_env to the real job-running user right after it is acquired, so any deferred computation flushed during the job's execution uses a valid environment.

Problem

Jobs executed through the built-in /queue_job/runjob HTTP endpoint can
intermittently — but for some jobs, deterministically — fail with:

ValueError: Expected singleton: res.users()

raised from deep inside unrelated code (a has_group() call, a computed
field, any @api.constrains method — anywhere self.env.user is accessed).
The job's own code is correct; nothing in the job itself touches env.user
incorrectly. The failure happens later, during an unrelated flush.

Root cause

/queue_job/runjob is declared auth="none". For every auth="none"
route, Odoo's IrHttp._auth_method_none() runs before the controller body,
and does this unconditionally:

request.env=api.Environment(request.env.cr, None, request.env.context)
request.env.transaction.default_env=request.env

This pins the transaction'sdefault_env to an environment with
uid=None, bypassing the truthy-uid guard that normally protects
Transaction.default_env assignment (Environment.__new__,
odoo/orm/environments.py).

runjob() then builds its own, correctly-scoped environment
(env = http.request.env(user=SUPERUSER_ID)) and runs the job with it —
but this is a differentEnvironment object. It does not touch
transaction.default_env, which stays pinned to the uid=None one set by
_auth_method_none().

The crash surfaces later because Cursor.flush() always delegates to the
transaction'sdefault_env, not to whichever environment happened to
schedule the pending computation:

defflush(self) ->None:
ifself.default_envisnotNone:
self.default_env.flush_all()
...

So any deferred field computation — a stored compute, an
@api.constrains method, anything reached through the generic
_compute_field_value_validate_fields chain — that is still pending
when a with cr.savepoint(): block inside the job exits (which triggers an
implicit flush) executes against the broken uid=None environment. If that
code path happens to read self.env.user, env.user resolves to
browse(None), an empty recordset — not a missing/inactive user, an
actually empty one — and ensure_one() (e.g. inside has_group()) raises.

Whether a given job reproduces this depends entirely on whether some
computation happens to still be pending exactly at that savepoint-exit
flush.

`IrHttp._auth_method_none()` forces `transaction.default_env` to an
environment with `uid=None` for every `auth="none"` route, including
`/queue_job/runjob`. Any `env.user` access during a savepoint-triggered
flush inside a job (e.g. a recompute/constrain calling `has_group()`)
then raises "Expected singleton: res.users()", since `browse(None)`
is an empty recordset.
Repair `default_env` to the real job-running user right after it is
acquired, so any deferred computation flushed during the job's
execution uses a valid environment.
@OCA-git-bot

Copy link
Copy Markdown
Contributor

Hi @guewen, @sbidoul,
some modules you are maintaining are being modified, check this out!

@RicardoalsoRicardoalso changed the title [FIX] queue_job: repair default_env in runjob[19.0][FIX] queue_job: repair default_env in runjobJul 15, 2026
@sbidoul

Copy link
Copy Markdown
Member

Duplicate of #923 ?

@Ricardoalso

Copy link
Copy Markdown
Author

Duplicate of #923 ?

I missed that one ! Thanks for pointing it out @sbidoul

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mod:queue_jobModule queue_jobseries:19.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Ricardoalso@OCA-git-bot@sbidoul