ruvnet/ruflo · critical
Failed to load any models from upstream
Error message
Failed to load any models from upstream
What it means
Thrown by applyModelState in models.ts during a model-cache refresh. After buildModels() fetches and normalizes the upstream /models response, applyModelState refuses to install the result if it contains zero models; this protects the running server from wiping a working registry (defaultModel, taskModel, validModelIdSchema) with an empty set.
Solutions
- Inspect the [models] logs: 'Parsed models count' shows how many entries survived parsing — if 0, inspect the raw response from ${OPENAI_BASE_URL}/models.
- Verify the gateway actually serves models: curl -H "Authorization: Bearer $OPENAI_API_KEY" $OPENAI_BASE_URL/models and check data.length.
- Check any processing/filter logic in buildModels that can drop models and loosen it if it excludes everything.
- Retry the refresh once the upstream repopulates; the old model list stays active until then.
Defensive patterns
Strategy: try-catch
Validate before calling
const upstream = await fetch(`${OPENAI_BASE_URL}/models`, { headers: { Authorization: `Bearer ${token}` } });
const list = await upstream.json();
if (!Array.isArray(list.data) || list.data.length === 0) {
throw new Error("Upstream returned no models; keeping previous cache");
} Try / catch
try {
await refreshModels();
} catch (err) {
// previous `models` array is untouched because applyModelState throws before assignment
logger.error({ err: String(err) }, "model refresh failed; serving cached models");
} Prevention
- Smoke-test the gateway's /models endpoint in CI so an empty list is caught before deploy.
- Keep the last-known-good model cache durable so a bad refresh never empties a running instance.
- Alert on refresh failures and on summary.total dropping to zero.
When it happens
Trigger: A refreshModels() cycle where the upstream returned HTTP 200 but an empty model list, or every entry was filtered out during processing (e.g., a filter on multimodal/tools/inference-api availability removed all models). The throw happens before `models = newModels`, so the previous registry survives.
Common situations: OpenAI-compatible gateway temporarily exposes no deployments (provider outage, all models disabled); misconfigured OPENAI_BASE_URL pointing to the right host but wrong path that yields an empty-but-valid listSchema payload; custom model filters in buildModels excluding everything.
Related errors
- Failed to fetch /models
- No models available to select task model
- Config manager is disabled
- Failed to fetch /models: (no auth token available)
- HTTP transport already running
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/998d1541c8ec47cf.
Report an issue: GitHub.
Appendix: source
Thrown at ruflo/src/ruvocal/src/lib/server/models.ts:246
prepromptUrl: model.prepromptUrl,
endpoints:
model.endpoints?.map((endpoint) => {
if (endpoint.type === "openai") {
const { type, baseURL } = endpoint;
return { type, baseURL };
}
return { type: endpoint.type };
}) ?? null,
multimodal: model.multimodal,
multimodalAcceptedMimetypes: model.multimodalAcceptedMimetypes,
supportsTools: (model as unknown as { supportsTools?: boolean }).supportsTools ?? false,
isRouter: model.isRouter,
hasInferenceAPI: model.hasInferenceAPI,
});
const applyModelState = (newModels: ProcessedModel[], startedAt: number): ModelsRefreshSummary => {
if (newModels.length === 0) {
throw new Error("Failed to load any models from upstream");
}
const previousIds = new Set(models.map((m) => m.id));
const previousSignatures = new Map(models.map((m) => [m.id, signatureForModel(m)]));
const refreshedAt = new Date();
const durationMs = Date.now() - startedAt;
models = newModels;
defaultModel = models[0];
taskModel = resolveTaskModel(models);
validModelIdSchema = createValidModelIdSchema(models);
lastModelRefresh = refreshedAt;
lastModelRefreshDurationMs = durationMs;
const added = newModels.map((m) => m.id).filter((id) => !previousIds.has(id));
const removed = Array.from(previousIds).filter(
(id) => !newModels.some((model) => model.id === id)
);View on GitHub (pinned to fa13ee4ad6)