{"record":{"id":"743ce8f2afa3cc34","repo":"ruvnet/ruflo","slug":"duplicate-route-name-r-name","errorCode":null,"errorMessage":"Duplicate route name: ${r.name}","messagePattern":"Duplicate route name: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ruflo/src/ruvocal/src/lib/server/router/policy.ts","lineNumber":21,"sourceCode":"import type { Route } from \"./types\";\n\nlet ROUTES: Route[] = [];\nlet loaded = false;\n\nexport async function loadPolicy(): Promise<Route[]> {\n\tconst path = config.LLM_ROUTER_ROUTES_PATH;\n\tconst text = await readFile(path, \"utf8\");\n\tconst arr = JSON.parse(text) as Route[];\n\tif (!Array.isArray(arr)) {\n\t\tthrow new Error(\"Routes config must be a flat array of routes\");\n\t}\n\tconst seen = new Set<string>();\n\tfor (const r of arr) {\n\t\tif (!r?.name || !r?.description || !r?.primary_model) {\n\t\t\tthrow new Error(`Invalid route entry: ${JSON.stringify(r)}`);\n\t\t}\n\t\tif (seen.has(r.name)) {\n\t\t\tthrow new Error(`Duplicate route name: ${r.name}`);\n\t\t}\n\t\tseen.add(r.name);\n\t}\n\tROUTES = arr;\n\tloaded = true;\n\treturn ROUTES;\n}\n\nexport async function getRoutes(): Promise<Route[]> {\n\tif (!loaded) await loadPolicy();\n\treturn ROUTES;\n}\n\nexport function resolveRouteModels(\n\trouteName: string,\n\troutes: Route[],\n\tfallbackModel: string\n): { candidates: string[] } {","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/ruflo/src/ruvocal/src/lib/server/router/policy.ts#L3-L39","documentation":"Thrown by loadPolicy() after the per-entry validation passes, during a deduplication sweep that tracks each route's name in a Set. Routes are selected by name elsewhere (resolveRouteModels does routes.find(r => r.name === routeName)), so non-unique names would make routing ambiguous; the loader refuses to proceed instead.","triggerScenarios":"The routes JSON contains two objects whose name field is byte-for-byte identical (case-sensitive). The second occurrence triggers the throw on seen.has(r.name).","commonSituations":"Copying a route entry as a template and forgetting to rename it; merging two route files that both define a \"casual_conversation\" route; case differences (\"Chat\" vs \"chat\") are NOT duplicates but visually confusing ones.","solutions":["Search the routes JSON for the duplicated name printed in the error.","Rename one of the entries so every name is unique (names are the lookup key for resolveRouteModels).","If the duplication is intentional (e.g. A/B variants), encode the variant in the name (\"chat_v2\") instead.","Re-run loadPolicy(); dedup is re-evaluated from scratch on each load."],"exampleFix":"// before\n[\n  { \"name\": \"chat\", \"description\": \"casual\", \"primary_model\": \"gpt-4o\" },\n  { \"name\": \"chat\", \"description\": \"reasoning\", \"primary_model\": \"o1\" }\n]\n// after\n[\n  { \"name\": \"chat\", \"description\": \"casual\", \"primary_model\": \"gpt-4o\" },\n  { \"name\": \"chat_reasoning\", \"description\": \"reasoning\", \"primary_model\": \"o1\" }\n]","handlingStrategy":"validation","validationCode":"const arr = JSON.parse(text) as Route[];\nconst names = arr.map((r) => r.name);\nconst dupes = names.filter((n, i) => names.indexOf(n) !== i);\nif (dupes.length) {\n  throw new Error(`Duplicate route names in config: ${[...new Set(dupes)].join(\", \")}`);\n}","typeGuard":"function hasUniqueNames(routes: { name: string }[]): boolean {\n  return new Set(routes.map((r) => r.name)).size === routes.length;\n}","tryCatchPattern":"try {\n  await loadPolicy();\n} catch (e) {\n  if (String(e?.message ?? \"\").startsWith(\"Duplicate route name\")) {\n    // surface the conflicting names to the operator, then exit\n    console.error(e.message);\n    process.exit(1);\n  }\n  throw e;\n}","preventionTips":["Lint the routes file for duplicate name keys in a pre-commit hook.","Use a single source of truth (e.g. code-generated from a typed list) so duplicates surface as a compile error.","Run the dedupe check in a schema validator (uniqueItems on a name-only projection)."],"tags":["config","validation","router","duplicate"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}