ZhuLinsen/daily_stock_analysis · error · ValueError
Responses API surface requires a normalized openai/<model> r
Error message
Responses API surface requires a normalized openai/<model> route; got {normalized_model!r} What it means
Config-time validation in apply_litellm_api_surface (src/config.py): when a channel declares api_surface='responses', the wire model must already be an explicit openai-prefixed route (get_explicit_llm_channel_model_provider(model) == 'openai'). The Responses API surface is bridged by rewriting 'openai/<model>' into 'openai/responses/<model>' for LiteLLM; a provider-less bare model name or another provider (anthropic/..., bedrock/...) cannot be bridged this way, so the config is rejected.
Source
Thrown at src/config.py:491
return raw_prefix
if canonical_prefix in providers:
return canonical_prefix
return ""
def apply_litellm_api_surface(model: str, api_surface: Optional[str]) -> str:
"""Encode an explicit API surface in a LiteLLM wire model.
LiteLLM's ``provider/responses/model`` convention keeps the public Router
alias stable while letting ``completion()`` bridge messages, streaming,
tools, responses, and usage through the provider's Responses endpoint.
"""
normalized_model = (model or "").strip()
if not normalized_model or normalize_llm_channel_api_surface(api_surface) != "responses":
return normalized_model
provider = get_explicit_llm_channel_model_provider(normalized_model)
if provider != "openai":
raise ValueError(
"Responses API surface requires a normalized openai/<model> route; "
f"got {normalized_model!r}"
)
provider, remainder = normalized_model.split("/", 1)
if remainder.startswith("responses/"):
return normalized_model
return f"{provider}/responses/{remainder}"
def resolve_llm_channel_protocol(
protocol: Optional[str],
*,
base_url: Optional[str] = None,
models: Optional[List[str]] = None,
channel_name: Optional[str] = None,
) -> str:
"""Resolve the effective protocol for a channel."""
explicit = canonicalize_llm_channel_protocol(protocol)View on GitHub (pinned to 5159bd72e8)
Solutions
- Use an explicit openai route: set model to 'openai/gpt-4o' (or 'openai/responses/gpt-4o') on the channel using api_surface=responses.
- If the target provider is not OpenAI, remove api_surface=responses and use the provider's native chat/completions surface.
- Verify the channel YAML/env after fixing by re-running config parsing before restarting the service.
Example fix
# before models: ["gpt-4o"] api_surface: responses # after models: ["openai/gpt-4o"] api_surface: responses
Defensive patterns
Strategy: validation
Validate before calling
from src.config import get_explicit_llm_channel_model_provider
if api_surface == "responses":
assert get_explicit_llm_channel_model_provider(model) == "openai", (
f"responses surface needs openai/<model>, got {model!r}"
) Type guard
def is_openai_route(model: str) -> bool:
return get_explicit_llm_channel_model_provider(model) == "openai" Try / catch
try:
wire = apply_litellm_api_surface(model, api_surface)
except ValueError as exc:
raise ConfigError(str(exc)) from exc Prevention
- Always prefix OpenAI models with openai/ in channel config
- Reserve api_surface=responses for OpenAI channels only
- Validate channel YAML in CI with a config-parse smoke test
When it happens
Trigger: Setting api_surface: responses (or API_SURFACE=responses) on a channel whose model is 'gpt-4o' (no provider prefix), 'anthropic/claude-3', or any non-openai provider route. The function raises before any router is built, so this fails at config load/parse time.
Common situations: Copy-pasting a Responses-API example onto a non-OpenAI channel; assuming LiteLLM's responses bridging works for all providers; forgetting the 'openai/' prefix when migrating from OpenAI direct config.
Related errors
- LLM route aliases cannot mix API surfaces: {sorted(surface_c
- 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
- unsafe_config
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/df6acbf48c2f5f7c.
Report an issue: GitHub.