- Issue: OpenRouter requests failed with HTTP
400because themodels[]array exceeded size constraints.
Bug Analysis & Root Cause
- Observed Error:
"'models' array must have 3 items or fewer." - Failure Path: UI generation ->
src/scripts/api.jsbuildApiPayload(...)->POST /api/llm-> OpenRouter. - Root Cause: OpenRouter fallback routing was built as
[primaryModel, ...fallbackModels]without size checks. 1 primary plus 3 fallback models produced an array of length 4, exceeding OpenRouter’s maximum limit of 3 items.
Proposed Fix
- Client Payload Cap and Deduplication (
src/scripts/api.js):- Cap routing models:
const OPENROUTER_MAX_ROUTING_MODELS = 3; - Normalize keys to deduplicate standard and
:onlinevariants. - Filter candidates against synced model catalog.
const candidates = [primaryModel, ...providerConfig.fallbackModels] .map((model) => String(model || '').trim()) .filter((model) => isModelAvailableInCatalog(settings.provider, model)) .filter(Boolean); const deduped = []; const seen = new Set(); candidates.forEach((model) => { const key = String(model || '').trim().replace(/:online$/i, ''); if (!key || seen.has(key)) return; seen.add(key); deduped.push(model); }); const limited = deduped.slice(0, OPENROUTER_MAX_ROUTING_MODELS); if (limited.length > 1) { payload.models = limited; } - Cap routing models:
- Server-Side Defensive Clamp (
server.py):- Intercept OpenRouter payload proxying.
- Parse, deduplicate standard/online variants, and clamp array length to 3 elements.
if provider == "openrouter": models = payload.get("models") if isinstance(models, list): deduped_models = [] seen = set() for model in models: model_id = str(model or "").strip() if not model_id: continue routing_key = model_id.replace(":online", "") if not routing_key or routing_key in seen: continue seen.add(routing_key) deduped_models.append(model_id) if len(deduped_models) >= 3: break if len(deduped_models) > 1: payload["models"] = deduped_models else: payload.pop("models", None)
Explanation of Fix
- Client Safety: Guarantees the payload array respects the maximum size constraints.
- Server Enforcement: Guards against any legacy or malformed client calls.
- Key Normalization: Avoids duplicate routing options for similar models.
Verification Steps
- Run Backend:
python3 server.py. - Trigger Call: Generate tree using OpenRouter inside client.
- Verify API Code: Confirm HTTP 200 is returned.
- Endpoint Mocking: Send payload with 4 models to proxy to verify clamping.
- Syntax Checks:
node --check src/scripts/api.jspython3 -m py_compile server.py