ZhuLinsen/daily_stock_analysis · error · ValueError
LLM route aliases cannot mix API surfaces: {sorted(surface_c
Error message
LLM route aliases cannot mix API surfaces: {sorted(surface_conflicts)} What it means
Config-time validation in _channels_to_model_list (src/config.py): find_llm_channel_surface_conflicts detects that the same public route alias (the normalized model name LiteLLM Router routes on) is declared by more than one enabled channel with different api_surface values (e.g. 'responses' vs 'chat'). LiteLLM's Router keys deployments by model_name, so one alias with two surfaces is ambiguous — requests could hit either wire format nondeterministically. The conflicting alias names are embedded in the message.
Source
Thrown at src/config.py:2570
str(channel.get("base_url") or ""),
)
for model in channel.get("models") or []
}.intersection(conflicting_models)
]
return channels, issues, blocks_legacy_fallback, blocked_hermes_routes
@classmethod
def _channels_to_model_list(cls, channels: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""Convert parsed LLM channels to LiteLLM Router model_list format.
Mapping follows:
- LiteLLM providers: https://docs.litellm.ai/docs/providers
- LiteLLM model_list 语义: https://docs.litellm.ai/docs/proxy/configs#the-model_list-key
"""
surface_conflicts = find_llm_channel_surface_conflicts(channels)
if surface_conflicts:
raise ValueError(
"LLM route aliases cannot mix API surfaces: "
+ ", ".join(sorted(surface_conflicts))
)
model_list: List[Dict[str, Any]] = []
for ch in channels:
hermes_refs = {
str(ref.get("route_model") or ""): ref
for ref in (ch.get("model_refs") or [])
if isinstance(ref, dict)
}
for model_name in ch['models']:
for api_key in ch['api_keys']:
model_ref = hermes_refs.get(str(model_name))
wire_model = str((model_ref or {}).get("wire_model") or model_name)
api_surface = normalize_llm_channel_api_surface(ch.get("api_surface"))
wire_model = apply_litellm_api_surface(wire_model, api_surface)
litellm_params: Dict[str, Any] = {
'model': wire_model,View on GitHub (pinned to 5159bd72e8)
Solutions
- Give each surface a distinct route alias (e.g. 'openai/gpt-4o' for chat and 'openai/gpt-4o-responses' for responses) so no alias maps to two surfaces.
- Or remove/disable (enabled: false) the old-surface channel declaring the same alias.
- Search all channel configs for the aliases listed in the error message to find both declarations.
Example fix
# before (two channels share alias 'openai/gpt-4o') channel A: models=["openai/gpt-4o"], api_surface: responses channel B: models=["openai/gpt-4o"] # after channel A: models=["openai/gpt-4o-resp"], api_surface: responses channel B: models=["openai/gpt-4o"]
Defensive patterns
Strategy: validation
Validate before calling
from src.config import find_llm_channel_surface_conflicts
conflicts = find_llm_channel_surface_conflicts(channels)
if conflicts:
raise SystemExit(f"alias/surface conflicts: {sorted(conflicts)}") Try / catch
try:
model_list = Config._channels_to_model_list(channels)
except ValueError as exc:
if "mix API surfaces" in str(exc):
# rename or disable the conflicting alias
raise Prevention
- One route alias = one api_surface; introduce a new alias when migrating surfaces
- Run find_llm_channel_surface_conflicts as a preflight check on channel configs
- Disable (enabled: false) old channels instead of leaving duplicates
When it happens
Trigger: Two enabled channels both listing model 'openai/gpt-4o' but one with api_surface: responses and the other without (or 'chat'); or a channel re-declaring an alias after protocol/base_url normalization with a different surface. Raised while building the Router model_list, i.e. at startup/config load.
Common situations: Adding a new Responses-API channel while keeping the old chat-completions channel for 'migration'; YAML merge duplicating a channel block with edited surface; copy-paste between staging and production configs.
Related errors
- Responses API surface requires a normalized openai/<model> r
- Hermes/non-Hermes mixed generation route is not supported wi
- Hermes only supports PROTOCOL=openai
- Hermes BASE_URL must point to 127.0.0.1, localhost, or [::1]
- Hermes BASE_URL contains an invalid port
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/c98cdb384020305f.
Report an issue: GitHub.