{"record":{"id":"8f68420c49d50399","repo":"ruvnet/ruflo","slug":"no-models-available-to-select-task-model","errorCode":null,"errorMessage":"No models available to select task model","messagePattern":"No models available to select task model","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ruflo/src/ruvocal/src/lib/server/models.ts","lineNumber":206,"sourceCode":"\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}\n\n\treturn modelList[0];\n};\n\nconst signatureForModel = (model: ProcessedModel) =>\n\tJSON.stringify({\n\t\tdescription: model.description,\n\t\tdisplayName: model.displayName,","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/ruflo/src/ruvocal/src/lib/server/models.ts#L188-L224","documentation":"Thrown by resolveTaskModel when the supplied modelList is empty. resolveTaskModel picks the model used for background tasks (summaries, titles, tooling) using config.TASK_MODEL when set, otherwise models[0]. Like createValidModelIdSchema, it is called from applyModelState after the empty-list guard, so reaching it means it was called without that guard or models were cleared concurrently.","triggerScenarios":"Direct call to resolveTaskModel([]); or a code path that resets the global models array to [] and then calls refreshModels/resolveTaskModel. Normally unreachable because applyModelState checks newModels.length === 0 first (throwing error 34 instead).","commonSituations":"Refactor that introduces a second call site for resolveTaskModel without an empty check; test harness that stubs models as []; hot-reload clearing module state during development.","solutions":["Only call resolveTaskModel after confirming modelList.length > 0.","If config.TASK_MODEL references a model that is not in the list, log a warning and fall back to models[0] rather than failing - but only when the list itself is non-empty.","Keep the applyModelState sequencing intact so this guard is exercised before resolveTaskModel."],"exampleFix":"// before\ntaskModel = resolveTaskModel(models);\n\n// after\nif (models.length === 0) {\n  throw new Error('Cannot resolve task model: model list is empty');\n}\ntaskModel = resolveTaskModel(models);","handlingStrategy":"validation","validationCode":"function safeResolveTaskModel(models: ProcessedModel[]) {\n  if (models.length === 0) throw new Error(\"Cannot resolve task model: empty list\");\n  return resolveTaskModel(models);\n}","typeGuard":"function isNonEmpty<T>(list: readonly T[]): list is readonly [T, ...T[]] {\n  return list.length > 0;\n}","tryCatchPattern":"try { taskModel = resolveTaskModel(models); }\ncatch (e) {\n  if (e instanceof Error && /No models available to select task model/.test(e.message)) {\n    // skip task model update; keep previous value\n  } else throw e;\n}","preventionTips":["Route all task-model resolution through applyModelState so the upstream empty guard fires first.","If config.TASK_MODEL is set, validate that the id exists in the loaded list and warn if missing.","Avoid manually resetting the global models array to [] outside of applyModelState."],"tags":["models","task-model","startup","defensive"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}