ruvnet/ruflo · error · Error
Invalid route entry: ${JSON.stringify(r)}
Error message
Invalid route entry: ${JSON.stringify(r)} What it means
Thrown by loadPolicy() while validating each entry of the LLM router routes JSON (config.LLM_ROUTER_ROUTES_PATH). The Route interface requires non-empty name, description, and primary_model fields; the guard uses truthiness (!r?.name || !r?.description || !r?.primary_model), so a missing key, null, or an empty string all trip it. The thrown message embeds JSON.stringify(r) so the offending object is visible in the error text.
Source
Thrown at ruflo/src/ruvocal/src/lib/server/router/policy.ts:18
import { readFile } from "node:fs/promises";
import { config } from "$lib/server/config";
import type { Route } from "./types";
let ROUTES: Route[] = [];
let loaded = false;
export async function loadPolicy(): Promise<Route[]> {
const path = config.LLM_ROUTER_ROUTES_PATH;
const text = await readFile(path, "utf8");
const arr = JSON.parse(text) as Route[];
if (!Array.isArray(arr)) {
throw new Error("Routes config must be a flat array of routes");
}
const seen = new Set<string>();
for (const r of arr) {
if (!r?.name || !r?.description || !r?.primary_model) {
throw new Error(`Invalid route entry: ${JSON.stringify(r)}`);
}
if (seen.has(r.name)) {
throw new Error(`Duplicate route name: ${r.name}`);
}
seen.add(r.name);
}
ROUTES = arr;
loaded = true;
return ROUTES;
}
export async function getRoutes(): Promise<Route[]> {
if (!loaded) await loadPolicy();
return ROUTES;
}
export function resolveRouteModels(
routeName: string,View on GitHub (pinned to 6b01dc5a68)
Solutions
- Open the file at config.LLM_ROUTER_ROUTES_PATH and inspect the exact object printed in the error message.
- Ensure every entry has non-empty string values for name, description, and primary_model (the three required Route fields).
- If you renamed primary_model to model for brevity, change it back; fallback_models is optional but primary_model is not.
- Re-run loadPolicy()/restart the server; the loader caches ROUTES only after full validation passes.
Example fix
// before (routes.json)
[{ "name": "chat", "description": "casual chat", "model": "gpt-4o" }]
// after
[{ "name": "chat", "description": "casual chat", "primary_model": "gpt-4o", "fallback_models": ["gpt-4o-mini"] }] Defensive patterns
Strategy: validation
Validate before calling
import Ajv from "ajv";
const routeSchema = {
type: "array",
items: {
type: "object",
required: ["name", "description", "primary_model"],
properties: {
name: { type: "string", minLength: 1 },
description: { type: "string", minLength: 1 },
primary_model: { type: "string", minLength: 1 },
fallback_models: { type: "array", items: { type: "string" } },
},
additionalProperties: false,
},
};
const validate = new Ajv({ allErrors: true }).compile(routeSchema);
const arr = JSON.parse(await readFile(path, "utf8"));
if (!validate(arr)) {
throw new Error("Invalid routes config: " + JSON.stringify(validate.errors));
} Type guard
import type { Route } from "./types";
function isRoute(x: unknown): x is Route {
return (
typeof x === "object" && x !== null &&
typeof (x as Route).name === "string" && (x as Route).name !== "" &&
typeof (x as Route).description === "string" && (x as Route).description !== "" &&
typeof (x as Route).primary_model === "string" && (x as Route).primary_model !== ""
);
} Try / catch
try {
await loadPolicy();
} catch (e) {
console.error("LLM router routes failed to load from", config.LLM_ROUTER_ROUTES_PATH, String(e?.message ?? e));
process.exit(1); // fatal at startup; do not serve with an empty/invalid policy
} Prevention
- Treat the routes file as typed config: validate it with a JSON schema in CI before deploy.
- Write a unit test that loadPolicy() succeeds against the committed sample routes file.
- Use additionalProperties:false in the schema so a typo like "model" is caught early.
When it happens
Trigger: Server startup or the first getRoutes() call (lazy load) parses a routes file where at least one object omits primary_model, or uses the wrong key name (e.g. "model" instead of "primary_model"), or has name/description set to "".
Common situations: Editing the routes JSON by hand and misspelling a field; migrating from an older schema that used "model"; copy-pasting a route and deleting a line; setting LLM_ROUTER_ROUTES_PATH to a stale or template file that was never filled in.
Related errors
- Routes config must be a flat array of routes
- Duplicate route name: ${r.name}
- No models available to build validation schema
- Failed to load any models from upstream
- OPENAI_BASE_URL not set
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/5840e477aceb2df2.
Report an issue: GitHub.