{"record":{"id":"a1fd45e90bbeec66","repo":"ruvnet/ruflo","slug":"no-models-available-to-build-validation-schema","errorCode":null,"errorMessage":"No models available to build validation schema","messagePattern":"No models available to build validation schema","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"ruflo/src/ruvocal/src/lib/server/models.ts","lineNumber":198,"sourceCode":"export let defaultModel!: ProcessedModel;\nexport let taskModel!: ProcessedModel;\nexport let validModelIdSchema: z.ZodType<string> = z.string();\nexport let lastModelRefresh = new Date(0);\nexport let lastModelRefreshDurationMs = 0;\nexport let lastModelRefreshSummary: ModelsRefreshSummary = {\n\trefreshedAt: new Date(0),\n\tdurationMs: 0,\n\tadded: [],\n\tremoved: [],\n\tchanged: [],\n\ttotal: 0,\n};\n\nlet inflightRefresh: Promise<ModelsRefreshSummary> | null = null;\n\nconst createValidModelIdSchema = (modelList: ProcessedModel[]): z.ZodType<string> => {\n\tif (modelList.length === 0) {\n\t\tthrow new Error(\"No models available to build validation schema\");\n\t}\n\tconst ids = new Set(modelList.map((m) => m.id));\n\treturn z.string().refine((value) => ids.has(value), \"Invalid model id\");\n};\n\nconst resolveTaskModel = (modelList: ProcessedModel[]) => {\n\tif (modelList.length === 0) {\n\t\tthrow new Error(\"No models available to select task model\");\n\t}\n\n\tif (config.TASK_MODEL) {\n\t\tconst preferred = modelList.find(\n\t\t\t(m) => m.name === config.TASK_MODEL || m.id === config.TASK_MODEL\n\t\t);\n\t\tif (preferred) {\n\t\t\treturn preferred;\n\t\t}\n\t}","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/ruflo/src/ruvocal/src/lib/server/models.ts#L180-L216","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check GET {OPENAI_BASE_URL}/models with the same token (Authorization: Bearer ...) — confirm it returns a non-empty data array","Fix or relax MODELS overrides so at least one model survives filtering","Verify the token has entitlements for the router/gateway and OPENAI_BASE_URL is the correct base (e.g. ends in /v1)","Fail startup with a clear message when the catalog is empty instead of letting the schema builder throw later"],"exampleFix":"// before\nconst parsed = listSchema.parse(json);\nconst models = parsed.data.map(processModel);\napplyModelState(models, startedAt); // empty list -> guard throws\n\n// after\nif (parsed.data.length === 0) {\n\tthrow new Error(\n\t\t`No models returned by ${baseURL}/models — check token entitlements and MODELS overrides`\n\t);\n}","handlingStrategy":"validation","validationCode":"if (models.length === 0) {\n\tthrow new Error(\n\t\t`No models from ${OPENAI_BASE_URL}/models — check token entitlements and MODELS overrides`\n\t);\n}\nvalidModelIdSchema = createValidModelIdSchema(models);","typeGuard":"function hasModels(list: unknown[]): list is [unknown, ...unknown[]] {\n\treturn list.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Health-check {OPENAI_BASE_URL}/models in CI and on boot; fail fast on an empty catalog","Keep at least one known-good model entry (override or gateway default) as a floor","Alert when refreshModels() reports total: 0 rather than waiting for schema construction to throw"],"tags":["models","zod","schema","empty-registry","startup"],"backgroundTag":"no-models-configured","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}