{"record":{"id":"07894dab46da43a9","repo":"ruvnet/ruflo","slug":"no-endpoints-configured-this-build-requires-opena","errorCode":null,"errorMessage":"No endpoints configured. This build requires OpenAI-compatible endpoints.","messagePattern":"No endpoints configured\\. This build requires OpenAI-compatible endpoints\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ruflo/src/ruvocal/src/lib/server/models.ts","lineNumber":135,"sourceCode":"\t\treturn parts.join(\"\\n\\n\");\n\t};\n}\n\nconst processModel = async (m: ModelConfig) => ({\n\t...m,\n\tchatPromptRender: await getChatPromptRender(m),\n\tid: m.id || m.name,\n\tdisplayName: m.displayName || m.name,\n\tpreprompt: m.prepromptUrl ? await fetch(m.prepromptUrl).then((r) => r.text()) : m.preprompt,\n\tparameters: { ...m.parameters, stop_sequences: m.parameters?.stop },\n\tunlisted: m.unlisted ?? false,\n});\n\nconst addEndpoint = (m: Awaited<ReturnType<typeof processModel>>) => ({\n\t...m,\n\tgetEndpoint: async (): Promise<Endpoint> => {\n\t\tif (!m.endpoints || m.endpoints.length === 0) {\n\t\t\tthrow new Error(\"No endpoints configured. This build requires OpenAI-compatible endpoints.\");\n\t\t}\n\t\t// Only support OpenAI-compatible endpoints in this build\n\t\tconst endpoint = m.endpoints[0];\n\t\tif (endpoint.type !== \"openai\") {\n\t\t\tthrow new Error(\"Only 'openai' endpoint type is supported in this build\");\n\t\t}\n\t\treturn await endpoints.openai({ ...endpoint, model: m });\n\t},\n});\n\ntype InternalProcessedModel = Awaited<ReturnType<typeof addEndpoint>> & {\n\tisRouter: boolean;\n\thasInferenceAPI: boolean;\n};\n\nconst inferenceApiIds: string[] = [];\n\nconst getModelOverrides = (): ModelOverride[] => {","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/ruflo/src/ruvocal/src/lib/server/models.ts#L117-L153","documentation":"This build of chat-ui speaks exclusively to OpenAI-compatible APIs, so every processed model must carry at least one endpoint. getEndpoint() (models.ts:135), invoked lazily when a generation actually starts, throws when a model's endpoints array is missing or empty — the model was listed/selectable but cannot be served.","triggerScenarios":"Selecting a model whose config has no endpoints: a MODELS env override entry without an \"endpoints\" array (or with \"endpoints\": []), or a model synthesized from /v1/models plus overrides such that no endpoint was attached. The throw happens on message send, not at startup.","commonSituations":"Hand-written MODELS JSON copied from other chat-ui forks where endpoints were optional; an override meant to tweak a model accidentally replacing its endpoints; a router returning model ids that no longer match the override shapes so endpoint attachment is skipped.","solutions":["Add an explicit endpoint to the model entry: \"endpoints\": [{ \"type\": \"openai\", \"baseURL\": \"...\", \"apiKey\": \"...\" }]","Or remove the broken MODELS override so models get the default OpenAI endpoint built from OPENAI_BASE_URL/OPENAI_API_KEY","Validate overrides at startup (every entry has endpoints?.length > 0) and log offending model ids before users hit it","If the model should not be selectable, mark it unlisted or remove it from the registry"],"exampleFix":"// before (.env)\nMODELS=[{\"id\":\"my-model\",\"name\":\"My Model\"}]  // getEndpoint() throws at generation time\n\n// after\nMODELS=[{\n\t\"id\":\"my-model\",\n\t\"name\":\"My Model\",\n\t\"endpoints\":[{\"type\":\"openai\",\"baseURL\":\"https://router.huggingface.co/v1\"}]\n}]","handlingStrategy":"validation","validationCode":"function assertModelServable(m: { endpoints?: { type: string }[] }): void {\n\tif (!m.endpoints || m.endpoints.length === 0) {\n\t\tthrow new Error(`Model ${String(m)} has no endpoints and cannot serve generations`);\n\t}\n}\nassertModelServable(model);\nconst endpoint = await model.getEndpoint();","typeGuard":"type ServableModel = { endpoints: { type: string }[] } & Record<string, unknown>;\nfunction isServable(m: { endpoints?: unknown[] }): m is ServableModel {\n\treturn Array.isArray(m.endpoints) && m.endpoints.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Validate MODELS overrides at startup: every entry must include a non-empty endpoints array","Filter unservable models out of the picker (unlisted) so users never select them","Log model id alongside the error — getEndpoint() throws long after config load"],"tags":["models","endpoints","configuration","openai","lazy-init"],"backgroundTag":"missing-endpoint-config","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"}