ruvnet/ruflo · error

Invalid route entry

Error message

Invalid route entry: ${JSON.stringify(r)}

What it means

Thrown by loadPolicy() while validating each entry of the routes array. Every route must carry non-empty name, description, and primary_model; an entry missing any of these is echoed verbatim (JSON.stringify) in the message so the offending object is identifiable. This is a schema check on the router's routing table.

Solutions

  1. Find the exact object printed in the error message and add the missing field(s) with non-empty values.
  2. Cross-check field names against the Route type: name, description, primary_model (plus any optional fields).
  3. Validate the whole file before boot: jq 'map(select((.name // "" ) == "" or (.description // "") == "" or (.primary_model // "") == ""))' routes.json should print [].
  4. Ensure primary_model references an id that exists in the upstream model list, otherwise routing will fail later even after this check passes.

Example fix

// before
{ "name": "code", "primary_model": "qwen/qwen3-coder" }

// after
{ "name": "code", "description": "Coding and technical questions", "primary_model": "qwen/qwen3-coder" }
Defensive patterns

Strategy: validation

Validate before calling

const required = ["name", "description", "primary_model"] as const;
for (const r of routes) {
	for (const key of required) {
		if (!r?.[key]) throw new Error(`route missing ${key}: ${JSON.stringify(r)}`);
	}
}

Type guard

function isRoute(r: unknown): r is { name: string; description: string; primary_model: string } {
	const c = r as Record<string, unknown>;
	return (
		!!c &&
		typeof c.name === "string" && c.name.length > 0 &&
		typeof c.description === "string" && c.description.length > 0 &&
		typeof c.primary_model === "string" && c.primary_model.length > 0
	);
}

Prevention

When it happens

Trigger: A route object in the LLM_ROUTER_ROUTES_PATH file omits name, description, or primary_model, or sets them to empty strings/undefined (falsy). The loop fails on the first invalid entry.

Common situations: Hand-edited routes file where a field was deleted or left as a placeholder; export from a spreadsheet/LLM that dropped fields; renaming fields (e.g., model instead of primary_model) after reading older docs.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: 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 fa13ee4ad6)