[Fix] Lazy feature loading under SERVER_ROOT_PATH returns 404#27812
Conversation
LazyFeatureMiddleware compared the raw scope path against registered prefixes (e.g. /policies), so requests under a server root path like /api/v1/policies/... never matched, the feature never loaded, and the endpoint returned 404. Strip the configured root path before matching, normalizing trailing slashes and enforcing a component boundary so /api does not falsely match /apiv2.
Greptile SummaryThis PR fixes a 404 regression for lazy-loaded feature endpoints when the proxy is deployed under a non-empty
Confidence Score: 5/5Safe to merge — the change is narrowly scoped to path normalization inside the lazy-loading middleware and does not touch auth, routing logic, or any critical request path beyond feature-load triggering. The fix is a small, well-commented addition: a single string strip gated on a clearly defensive condition, with the computed value cached at construction time. The test suite covers the meaningful edge cases and no existing tests are weakened. No auth, data, or migration paths are affected. No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/proxy/_lazy_features.py | Caches normalized SERVER_ROOT_PATH in init and strips it from the request path before prefix matching; correctly handles trailing-slash, root-path-is-slash, and pre-stripped-by-proxy edge cases. |
| tests/test_litellm/proxy/test_proxy_server.py | Adds 7 parametrized cases covering the root-path strip; one case ("boundary check prevents false match") is labeled correctly but does not exercise the boundary guard in a way that would fail without it. |
Reviews (2): Last reviewed commit: "Cache normalized SERVER_ROOT_PATH at mid..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
SERVER_ROOT_PATH is a process-startup env var. Read it once in __init__ instead of calling get_server_root_path() + rstrip on every request that arrives before all lazy features have loaded.
|
@greptile |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 384fc0b. Configure here.
431daa1
into
litellm_internal_staging
[Fix] Lazy feature loading under SERVER_ROOT_PATH returns 404 (backport of #27812)
Relevant issues
Summary
Failure Path (Before Fix)
When the proxy is deployed under a non-empty
SERVER_ROOT_PATH(e.g./api/v1), requests to lazy-loaded endpoints such as/api/v1/policies/attachments/listor/api/v1/vector_store/listreturn 404. TheLazyFeatureMiddlewarematches the raw scope path against the registered prefixes (/policies,/vector_store/, etc.), so the prefix never matches, the feature module is never imported, and the route is never registered. Starlette's router then 404s because the route doesn't exist. Non-lazy routes work because they're registered at startup.Fix
Strip the configured server root path from the request path before the prefix check. The check normalizes a trailing slash on the env var, skips the strip when the value is
/, and uses a component boundary (prefix + "/") so/apidoes not falsely match/apiv2. If the path doesn't carry the prefix (reverse proxy already stripped it), it is left alone.Testing
SERVER_ROOT_PATH=/api/v1against/api/v1/policies/attachments/listand/api/v1/vector_store/list; both return 200 after the fix.test_root_path_handling) covering: prefix strip + match, trailing-slash env, pre-stripped path (reverse proxy), no root path, root path/, boundary collision (/apivs/apiv2), and unrelated paths under the root.uv run pytest tests/test_litellm/proxy/test_proxy_server.py::TestLazyFeatureMiddleware— 10 passed.Type
🐛 Bug Fix
✅ Test
Screenshots
Note
Medium Risk
Changes request-path matching in
LazyFeatureMiddlewarebased onSERVER_ROOT_PATH, which could affect when optional routers are imported/registered and thus routing behavior for some deployments.Overview
Fixes lazy feature loading when the proxy is deployed behind a non-empty
SERVER_ROOT_PATHby stripping the configured root path from the incoming request path before prefix/suffix matching, with normalization for trailing slashes and a boundary check to avoid false matches.Adds a parametrized test (
test_root_path_handling) covering root-path stripping, reverse-proxy pre-stripped paths,SERVER_ROOT_PATH='/'no-op behavior, boundary collisions, and non-matching paths.Reviewed by Cursor Bugbot for commit 384fc0b. Bugbot is set up for automated code reviews on this repo. Configure here.