ruvnet/ruflo · error

Duplicate route name

Error message

Duplicate route name: ${r.name}

What it means

Thrown by loadPolicy() when two route entries in the routes file share the same name. Route names are the lookup key for routing decisions and telemetry, so duplicates are rejected during validation rather than silently overwritten — the message names the conflicting route.

Solutions

  1. Rename one of the duplicated routes so every name is unique (grep the file for the name from the error).
  2. Check for duplicates mechanically: jq 'group_by(.name) | map(select(length > 1)) | map(.[0].name)' routes.json.
  3. If the duplicate was intentional to express priority, instead use the ordering or scoring fields of a single route.
  4. Reload/restart so loadPolicy() revalidates the file.

Example fix

// before
[ { "name": "code", ... }, { "name": "code", ... } ]

// after
[ { "name": "code", ... }, { "name": "code-heavy", ... } ]
Defensive patterns

Strategy: validation

Validate before calling

const names = routes.map((r) => r.name);
const dupes = names.filter((n, i) => names.indexOf(n) !== i);
if (dupes.length > 0) throw new Error(`duplicate route names: ${dupes.join(", ")}`);

Type guard

function hasUniqueNames(routes: { name: string }[]): boolean {
	return new Set(routes.map((r) => r.name)).size === routes.length;
}

Prevention

When it happens

Trigger: The JSON array at LLM_ROUTER_ROUTES_PATH contains two objects with an identical non-empty name (e.g., copy-pasting a route and editing only the model).

Common situations: Duplicating an existing route to A/B test a different primary_model and forgetting to rename; merging two hand-maintained route files that both define a 'default' route.

Related errors


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

Appendix: source

Thrown at ruflo/src/ruvocal/src/lib/server/router/policy.ts:21

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,
	routes: Route[],
	fallbackModel: string
): { candidates: string[] } {

View on GitHub (pinned to fa13ee4ad6)