Skip to content

[19.0][FIX] endpoint_route_handler: build routing map without a bound request - #159

Merged
OCA-git-bot merged 1 commit into
OCA:19.0from
omar-d1:19.0-fix-routing-map-unbound-request
Sep 29, 2026
Merged

OCA-git-bot merged 1 commit into
OCA:19.0from
omar-d1:19.0-fix-routing-map-unbound-request

Conversation

@omar-d1

@omar-d1 omar-d1 commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Problem

routing_map() raises RuntimeError: object is not bound whenever it is called with no HTTP request in flight.

Both helpers this module adds to ir.http are classmethods, so neither has self.env, and both reach the endpoint registry through http.request.env:

@classmethod
def _endpoint_routing_rules(cls):
    e_registry = cls._endpoint_route_registry(http.request.env)

@classmethod
def _endpoint_route_last_version(cls):
    res = cls._get_routing_map_last_version(http.request.env)

The second one sits in the ormcache key of routing_map, so it runs before the wrapped body and the failure happens while computing the key.

How to reproduce

One line in odoo shell, on a database with endpoint_route_handler installed:

env['ir.http'].routing_map()
File ".../endpoint_route_handler/models/ir_http.py", line 48, in _endpoint_route_last_version
    res = cls._get_routing_map_last_version(http.request.env)
File ".../werkzeug/local.py", line 509, in _get_current_object
    raise RuntimeError(unbound_message)
RuntimeError: object is not bound

Odoo 19 reaches it through core too. website.technical.page builds its SQL view from get_static_routes(), which walks the routing map, and website's TestWebsiteTechnicalPage exercises that from a TransactionCase. On a database with this module installed, website_hr_recruitment's TestWebsiteHrRecruitmentTechnicalPage errors out. Crons and shell scripts hit the same wall.

The existing tests never caught it because they all wrap their calls in MockRequest, so a request was always present.

Fix

routing_map() is a model method, so self.env already holds a cursor. The request was never needed. Both helpers become instance methods reading self.env.

They are only ever called as self.<method>() from this module, and the tests already call _endpoint_route_last_version() on a recordset, so no call site changes.

last_version() reads a sequence, so any cursor on the database returns the same value and the cache key is unchanged. TestEndpointCrossEnv still passes: I checked two separate envs and both report the same version.

Test

Added a regression test that builds the map with no request bound.

@OCA-git-bot

Copy link
Copy Markdown
Contributor

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

@OCA-git-bot OCA-git-bot added series:19.0 mod:endpoint_route_handler Module endpoint_route_handler labels Aug 26, 2026
@omar-d1
omar-d1 force-pushed the 19.0-fix-routing-map-unbound-request branch from 1487ea5 to 9cb75ad Compare August 26, 2026 12:29

@simahawk simahawk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for you contrib. Do you think it makes sense to keep _get_routing_map_last_version as class method?

@Ricardoalso Ricardoalso left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @classmethod removal LGTM as we are now reading env from self

Version number is missing in the PR description

@omar-d1
omar-d1 force-pushed the 19.0-fix-routing-map-unbound-request branch from 9cb75ad to 4dbc27d Compare September 22, 2026 12:24
@omar-d1 omar-d1 changed the title [FIX] endpoint_route_handler: build routing map without a bound request [19.0][FIX] endpoint_route_handler: build routing map without a bound request Sep 22, 2026
@omar-d1

omar-d1 commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for you contrib. Do you think it makes sense to keep _get_routing_map_last_version as class method?

Yeah you're right i removed the classmethod from _get_routing_map_last_version and _endpoint_route_registry as well

@simahawk simahawk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@omar-d1 are you here at OCA days?

@OCA-git-bot

Copy link
Copy Markdown
Contributor

This PR has the approved label and has been created more than 5 days ago. It should therefore be ready to merge by a maintainer (or a PSC member if the concerned addon has no declared maintainer). 🤖

@SilvioC2C SilvioC2C left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, one minor possible improvement imho

@omar-d1

omar-d1 commented Sep 25, 2026

Copy link
Copy Markdown
Contributor Author

@omar-d1 are you here at OCA days?

Unfortunately not, stuck in the Dubai heat
Maybe next year tho 😀

routing_map() is wrapped with an ormcache whose key calls
_endpoint_route_last_version(), and the routing rules are injected by
_endpoint_routing_rules(). Both are classmethods, so neither has self.env
and both reach the endpoint registry through http.request.env. Off a
request that proxy is unbound and raises "RuntimeError: object is not
bound".

Odoo 19 calls routing_map() that way: website.technical.page builds its
SQL view from get_static_routes(), which walks the map, and website's
TestWebsiteTechnicalPage exercises it from a TransactionCase. Crons and
`odoo shell` hit the same wall. The existing tests never caught it
because they all wrap their calls in MockRequest.

routing_map() is a model method, so self.env already holds a cursor and
the request was never needed. Make the helpers instance methods reading
self.env: _endpoint_routing_rules() and _endpoint_route_last_version(),
plus the two they call, _endpoint_route_registry() and
_get_routing_map_last_version(), which only took an env argument because
their classmethod callers had none of their own. The classmethod shape
dates from 16.0, where core's routing_map() was itself a classmethod.
All call sites are in this module, and the tests already call
_endpoint_route_last_version() on a recordset.

last_version() reads a sequence, so any cursor on the database returns
the same value and the cache key is unchanged.

Add a regression test that builds the map with no request bound.
@omar-d1
omar-d1 force-pushed the 19.0-fix-routing-map-unbound-request branch from 4dbc27d to ef55d5d Compare September 25, 2026 11:03
@simahawk

Copy link
Copy Markdown
Contributor

/ocabot merge patch

@omar-d1 would you mind backporting to 18 pls?

@OCA-git-bot

Copy link
Copy Markdown
Contributor

This PR looks fantastic, let's merge it!
Prepared branch 19.0-ocabot-merge-pr-159-by-simahawk-bump-patch, awaiting test results.

@OCA-git-bot
OCA-git-bot merged commit 6208f48 into OCA:19.0 Sep 29, 2026
7 checks passed
@OCA-git-bot

Copy link
Copy Markdown
Contributor

Congratulations, your PR was merged at 796b6a9. Thanks a lot for contributing to OCA. ❤️

stupidgoats pushed a commit to stupidgoats/web-api that referenced this pull request Oct 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants