Skip to content

[19.0] env.user empty during flush/recompute inside queue jobs #922

@dannyadair

Description

@dannyadair

Module

queue_job

Describe the bug

https://github.com/OCA/queue/blob/19.0/queue_job/controllers/main.py#L218

env = http.request.env(user=SUPERUSER_ID)

I believe needs to be

http.request.update_env(user=SUPERUSER_ID)
env = http.request.env

runjob is declared as auth=none.
https://github.com/OCA/queue/blob/19.0/queue_job/controllers/main.py#L212
_auth_method_none forces uid=None for request env and default_env
https://github.com/odoo/odoo/blob/19.0/odoo/addons/base/models/ir_http.py#L263

So env = http.request.env(user=SUPERUSER_ID)
only produces a local env with that superuser, the other two are still None.

The job "itself" runs fine as the specified user, but any flush/recompute on a stored field uses the default_env with uid None.
We see a singleton error on is_backend_user = self.env.user.has_group('base.group_user') (in Odoo Enterprise compute_signing_user) because self.env.user is an empty recordset.

File ".../odoo/orm/environments.py", line 592, in flush
    self.default_env.flush_all()
...
File ".../enterprise/account_accountant/models/account_move.py", line 76, in _compute_signing_user
    is_backend_user = self.env.user.has_group('base.group_user')
File ".../odoo/addons/base/models/res_users.py", line 1074, in has_group
    self.ensure_one()
ValueError: Expected singleton: res.users()

The default_env mechanism was introduced 2 years ago
odoo/odoo@897e2f2
but the flush() was tolerant. Ironically,
odoo/odoo@45e1c05
(Jan 24 2025) states "flush as public user when there is no default_env"
but now _auth_method_none always sets default_env
odoo/odoo@45e1c05#diff-c5500b83255cdef56ed07cb5a74e9b0899ad378cf9a3739d9676101191c83eb2

Using update_env repoints the default_env correctly
https://github.com/odoo/odoo/blob/19.0/odoo/http.py#L1836

To Reproduce

Affected versions: 19.0

Steps to reproduce the behavior:

  1. Create a module with a computed attribute that accesses self.env.user (I added an explicit flush to force the pending compute):
class ResPartner(models.Model):
    _inherit = "res.partner"

    x_toucher = fields.Many2one("res.users", store=True, compute="_c")

    @api.depends("name")
    def _c(self):
        self.env.user.has_group("base.group_user")  # ensure_one() fails on empty recordset
        for r in self:
            r.x_toucher = self.env.user

    def touch(self):
        self.write({"name": "x"})
        self.env.cr.flush()  # forces Transaction.flush() -> default_env.flush_all()
  1. Enqueue a write that causes a flush: partner.with_delay().touch()
  2. Run the jobrunner -> job fails with Expected singleton: res.users().

Expected behavior
self.env.user inside stored-field recomputes triggered during a queue job should resolve to a real user (the job's user_id or SUPERUSER), not an empty recordset.

Additional context
In the same commit (45e1c05), core updated the two other places that create a uid=None env to set default_env explicitly:

  • odoo/service/common.py::exp_authenticate assigns env.transaction.default_env = env directly.
  • addons/bus/websocket.py added a new_env() helper that does the same.

Neither can use Request.update_env because they don't run inside an HTTP Request.

queue_job/controllers/main.py::runjob does run inside a Request, so the idiomatic fix is http.request.update_env(user=SUPERUSER_ID), which has been repointing default_env since 897e2f2.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions