Skip to content

fix(router): resolve model_group_alias on the fallback lookup path - #29378

Open
alvinttang wants to merge 1 commit into
BerriAI:litellm_oss_branchfrom
alvinttang:fix/model-group-alias-fallbacks
Open

fix(router): resolve model_group_alias on the fallback lookup path#29378
alvinttang wants to merge 1 commit into
BerriAI:litellm_oss_branchfrom
alvinttang:fix/model-group-alias-fallbacks

Conversation

@alvinttang

Copy link
Copy Markdown
Contributor

Summary

When a request is dispatched to a model defined via Router's model_group_alias (e.g. model_group_alias={"alias-model": "real-model"}), the configured fallbacks for the underlying real model are silently ignored. The router re-raises the original exception with:

No fallback model group found for original model_group=alias-model. Fallbacks=[{'real-model': ['my-good-model']}]

This is the verbatim error from #10317 (363 days old, 9 comments, multiple independent +1s).

Fixes #10317

Root cause

Router.async_function_with_fallbacks_common_utils captures model_group = kwargs["model"] — which is the alias name ("alias-model") — and passes it directly into get_fallback_model_group. Fallbacks are keyed by the real model group ("real-model"), so the exact-match lookup in fallback_event_handlers.py:64 never hits, falls through, and the router re-raises the original exception.

Router._get_model_from_alias already exists and is used on the request path to resolve aliases to real model names. The fallback path just doesn't call it.

Fix

litellm/router.py (10 LOC added): right after the early-exit guard in async_function_with_fallbacks_common_utils, call self._get_model_from_alias(model=model_group); if it returns non-None, swap model_group to the resolved real name. original_model_group is intentionally kept as the alias so user-facing logs and fallback headers remain stable for callers who watch them.

Tests

tests/local_testing/test_router_fallbacks.py::test_model_group_alias_respects_fallbacks (parametrised sync + async, 54 LOC). Sets:

  • model_group_alias={"alias-model": "real-bad-model"}
  • fallbacks=[{"real-bad-model": ["my-good-model"]}]

Fires with mock_testing_fallbacks=True, asserts the response model is the good fallback.

RED proof on main (exact error string from the bug report):

litellm.exceptions.InternalServerError: ... No fallback model group found for original model_group=alias-model. Fallbacks=[{'real-bad-model': ['my-good-model']}]

GREEN after patch: 2/2 pass (sync + async). Nearby logic-only fallback tests still pass (test_get_fallback_model_group, test_default_model_fallbacks, test_client_side_fallbacks_list, test_using_default_fallback, test_router_fallbacks_default_and_model_specific_fallbacks, test_router_disable_fallbacks_dynamically, test_router_fallbacks_with_wildcard_model_name). ruff check clean on the added lines.

Risk notes

  • _get_model_from_alias returns None for non-alias model names, so behaviour is unchanged when no model_group_alias is configured.
  • original_model_group keeps the user-facing alias for logs / fallback headers, so downstream metrics / observability are unaffected.
  • Only mutates model_group in the fallback-lookup scope; no state leaks out.

Refs #10317

@CLAassistant

CLAassistant commented May 31, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@codspeed-hq

codspeed-hq Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 16 untouched benchmarks


Comparing alvinttang:fix/model-group-alias-fallbacks (0b4fad3) with main (5be0797)

Open in CodSpeed

@greptile-apps

greptile-apps Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a long-standing bug where Router silently ignored configured fallbacks when a request was dispatched via model_group_alias. The alias name was passed directly into the fallback-group lookup, which is keyed by the real model group name, so the lookup always missed and the original exception was re-raised.

  • litellm/router.py: Adds a ~10-line alias-resolution block in async_function_with_fallbacks_common_utils that calls the existing _get_model_from_alias helper and rewrites model_group to the real name before the fallback lookup, while preserving original_model_group (the alias) for logs and fallback headers.
  • tests/local_testing/test_router_fallbacks.py: Adds a parametrised sync + async mock test that reproduces the exact error from the issue report and verifies the fallback fires correctly against the real model group.

Confidence Score: 4/5

Safe to merge; the fix is narrowly scoped to the fallback lookup path and has no effect when no alias is configured.

The alias resolution is correct and well-contained. The one gap is that all_deployments is still fetched with the alias name instead of the resolved real model group, meaning order-based fallbacks remain silently inactive for alias-targeted requests. This is a minor limitation that does not regress any existing behavior.

The deployment-lookup block in router.py around the all_deployments query (line 6378) could be improved to pass the resolved model group instead of the alias, but this is not a blocker.

Important Files Changed

Filename Overview
litellm/router.py Adds alias-to-real-model-group resolution before fallback lookup in async_function_with_fallbacks_common_utils; all downstream fallback paths (regular, context-window, content-policy, order-based external) now use the resolved name, but order-based deployment discovery still queries by the alias name.
tests/local_testing/test_router_fallbacks.py Adds a parametrised sync+async mock test that reproduces the exact alias-fallback failure and verifies the fix; uses mock_testing_fallbacks=True so no real network calls are made.

Comments Outside Diff (1)

  1. litellm/router.py, line 6377-6382 (link)

    P2 Order-based fallback deployment lookup still uses the alias name

    all_deployments is queried with original_model_group (the alias string, e.g. "alias-model") rather than the resolved model_group. Because deployments are registered under the real model group name, get_model_list(model_name="alias-model") returns an empty list, so _order_set is always empty and order-based intra-group fallbacks are silently bypassed for any alias-targeted request. The same resolved value used for the fallback-lookup fix could be passed here instead.

Reviews (1): Last reviewed commit: "fix(router): resolve model_group_alias b..." | Re-trigger Greptile

@codecov

codecov Bot commented May 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.00000% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
litellm/router.py 75.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@alvinttang
alvinttang changed the base branch from main to litellm_oss_branch May 31, 2026 14:43
@alvinttang
alvinttang force-pushed the fix/model-group-alias-fallbacks branch from 8f0fb32 to ac25bea Compare June 4, 2026 02:34
@alvinttang
alvinttang requested a review from a team June 4, 2026 02:34
@alvinttang

Copy link
Copy Markdown
Contributor Author

Force-pushed after rebasing onto the current litellm_oss_branch tip. The previous branch had drifted, which was making the Block fork dependency changes check pick up unrelated edits to uv.lock and the dashboard files. The PR is now back to the original 2-file, 64-line change (litellm/router.py + tests/local_testing/test_router_fallbacks.py).

@jrruethe

Copy link
Copy Markdown

I ran into this issue, hoping this fix gets merged!

When a request is routed via `model_group_alias`, the alias name was
passed straight to `get_fallback_model_group`, whose exact-match check
never matched fallbacks declared against the underlying real model
group. The router then raised the original exception with
"No fallback model group found for original model_group=<alias>",
silently dropping the configured fallback chain.

Resolve the alias to its real model group inside
`async_function_with_fallbacks_common_utils` before any fallback
lookup. `original_model_group` intentionally keeps the alias name so
logging and the `x-litellm-attempted-fallbacks` headers remain
user-facing.

Refs BerriAI#10317
@alvinttang
alvinttang force-pushed the fix/model-group-alias-fallbacks branch from ac25bea to c8d36ac Compare August 1, 2026 22:23
Comment thread litellm/router.py
# `get_fallback_model_group` works in both cases. `original_model_group`
# intentionally keeps the alias name for logging / fallback headers.
if model_group is not None:
aliased_model_group = self._get_model_from_alias(model=model_group)

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.

Low: Alias-specific fallback policy is bypassed

Rewriting model_group before lookup means a configuration containing both {"public-alias": ["safe-fallback"]} and {"real-group": ["other-fallback"]} will always select the latter for alias requests. A caller can deliberately trigger a content-policy or provider failure and route their request to a backend that was not configured for the public alias. Preserve alias-keyed policies by trying the original alias first, and only retry the lookup with the resolved model group when no alias-specific entry matches; apply the same precedence to general, context-window, and content-policy fallbacks.

@veria-ai

veria-ai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

PR overview

This PR updates the router’s fallback lookup path to resolve model group aliases when selecting configured fallback routes.

One issue remains in the lookup precedence: alias-specific fallback configuration can be skipped in favor of the resolved model group’s fallback. In configurations that define both policies, a caller who triggers a fallback condition could be routed to a backend not intended for the public alias.

Open issues (1)

Fixed/addressed: 0 · PR risk: 4/10

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants