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

  1. Inspect the [models] logs: 'Parsed models count' shows how many entries survived parsing — if 0, inspect the raw response from ${OPENAI_BASE_URL}/models.
  2. Verify the gateway actually serves models: curl -H "Authorization: Bearer $OPENAI_API_KEY" $OPENAI_BASE_URL/models and check data.length.
  3. Check any processing/filter logic in buildModels that can drop models and loosen it if it excludes everything.
  4. 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

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


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)