Skip to content

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

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.

Activity

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

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