ruvnet/ruflo · critical

No models available to build validation schema

Error message

No models available to build validation schema

What it means

createValidModelIdSchema (models.ts:198) builds the zod validator that later rejects unknown model ids in API requests; with an empty model list there is nothing to validate against, so it throws. It is called from applyModelState after refresh; note applyModelState already throws "Failed to load any models from upstream" for empty lists first, so this guard fires only via other paths that pass an empty registry.

Solutions

  1. Check GET {OPENAI_BASE_URL}/models with the same token (Authorization: Bearer ...) — confirm it returns a non-empty data array
  2. Fix or relax MODELS overrides so at least one model survives filtering
  3. Verify the token has entitlements for the router/gateway and OPENAI_BASE_URL is the correct base (e.g. ends in /v1)
  4. Fail startup with a clear message when the catalog is empty instead of letting the schema builder throw later

Example fix

// before
const parsed = listSchema.parse(json);
const models = parsed.data.map(processModel);
applyModelState(models, startedAt); // empty list -> guard throws

// after
if (parsed.data.length === 0) {
	throw new Error(
		`No models returned by ${baseURL}/models — check token entitlements and MODELS overrides`
	);
}
Defensive patterns

Strategy: validation

Validate before calling

if (models.length === 0) {
	throw new Error(
		`No models from ${OPENAI_BASE_URL}/models — check token entitlements and MODELS overrides`
	);
}
validModelIdSchema = createValidModelIdSchema(models);

Type guard

function hasModels(list: unknown[]): list is [unknown, ...unknown[]] {
	return list.length > 0;
}

Prevention

When it happens

Trigger: buildModels()/refreshModels() yielding zero usable models — {OPENAI_BASE_URL}/models returned an empty data array, every model was filtered out by MODELS overrides, or listSchema parsing produced [] — and the empty list reaching schema creation.

Common situations: OPENAI_BASE_URL pointing at a gateway/account with no model entitlements (valid token, empty catalog); overly restrictive MODELS overrides filtering everything; a router whose /models response shape changed so listSchema parses zero entries; fresh deploy before the upstream catalog is provisioned.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/a1fd45e90bbeec66. Report an issue: GitHub.

Appendix: source

Thrown at ruflo/src/ruvocal/src/lib/server/models.ts:198

export let defaultModel!: ProcessedModel;
export let taskModel!: ProcessedModel;
export let validModelIdSchema: z.ZodType<string> = z.string();
export let lastModelRefresh = new Date(0);
export let lastModelRefreshDurationMs = 0;
export let lastModelRefreshSummary: ModelsRefreshSummary = {
	refreshedAt: new Date(0),
	durationMs: 0,
	added: [],
	removed: [],
	changed: [],
	total: 0,
};

let inflightRefresh: Promise<ModelsRefreshSummary> | null = null;

const createValidModelIdSchema = (modelList: ProcessedModel[]): z.ZodType<string> => {
	if (modelList.length === 0) {
		throw new Error("No models available to build validation schema");
	}
	const ids = new Set(modelList.map((m) => m.id));
	return z.string().refine((value) => ids.has(value), "Invalid model id");
};

const resolveTaskModel = (modelList: ProcessedModel[]) => {
	if (modelList.length === 0) {
		throw new Error("No models available to select task model");
	}

	if (config.TASK_MODEL) {
		const preferred = modelList.find(
			(m) => m.name === config.TASK_MODEL || m.id === config.TASK_MODEL
		);
		if (preferred) {
			return preferred;
		}
	}

View on GitHub (pinned to fa13ee4ad6)