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

  1. Open the file at config.LLM_ROUTER_ROUTES_PATH and inspect the exact object printed in the error message.
  2. Ensure every entry has non-empty string values for name, description, and primary_model (the three required Route fields).
  3. If you renamed primary_model to model for brevity, change it back; fallback_models is optional but primary_model is not.
  4. 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

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


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/5840e477aceb2df2. Report an issue: GitHub.