ruvnet/ruflo · error · Error

unknown game "${key}". Known: ${Object.keys(GAMES).join(', '

Error message

unknown game "${key}". Known: ${Object.keys(GAMES).join(', ')}

What it means

Thrown by getGame() in the ruflo-arena domain when the supplied key is absent from the GAMES registry. The registry (plugins/ruflo-arena/src/domain/games.ts:50) is a fixed Record<string, GameSpec> populated at module load; lookups are exact string matches against its keys. The message lists the known keys at throw time so the caller can see the valid set.

Source

Thrown at plugins/ruflo-arena/src/domain/games.ts:59

  {
    '0|0': [1, -1],
    '1|1': [1, -1],
    '0|1': [-1, 1],
    '1|0': [-1, 1],
  },
  true,
);

export const GAMES: Record<string, GameSpec> = {
  'prisoners-dilemma': prisonersDilemma,
  pd: prisonersDilemma,
  'match-or-not': matchOrNot,
  mon: matchOrNot,
};

export function getGame(key: string): GameSpec {
  const g = GAMES[key];
  if (!g) throw new Error(`unknown game "${key}". Known: ${Object.keys(GAMES).join(', ')}`);
  return g;
}

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Use one of the exact keys printed in the error message: 'prisoners-dilemma', 'pd', 'match-or-not', or 'mon'.
  2. If you need a new game, add it to the GAMES Record in games.ts alongside its alias, then call getGame with the new key.
  3. Normalize inbound input before lookup: trim whitespace, lowercase, and map friendly names to canonical keys.
  4. Validate the key against Object.keys(GAMES) (or a Zod enum) at the API boundary so the error surfaces as a 400 rather than an uncaught throw.

Example fix

// before
const game = getGame(req.body.gameKey); // throws on typo

// after
import { GAMES, getGame } from './games';
const key = String(req.body.gameKey ?? '').trim().toLowerCase();
if (!(key in GAMES)) {
  return res.status(400).json({ error: `unknown game; valid: ${Object.keys(GAMES).join(', ')}` });
}
const game = getGame(key);
Defensive patterns

Strategy: validation

Validate before calling

import { GAMES } from './games';
const KNOWN_GAMES = Object.keys(GAMES);
function resolveGame(key: unknown): string | null {
  if (typeof key !== 'string') return null;
  const k = key.trim().toLowerCase();
  return KNOWN_GAMES.includes(k) ? k : null;
}
// before getGame:
const k = resolveGame(input);
if (!k) return res.status(400).json({ error: `valid games: ${KNOWN_GAMES.join(', ')}` });

Type guard

import { GAMES } from './games';
function isKnownGame(key: string): key is keyof typeof GAMES {
  return key in GAMES;
}

Try / catch

try { const game = getGame(key); } catch (e) { if (e instanceof Error && e.message.startsWith('unknown game')) return res.status(400).json({ error: e.message }); throw e; }

Prevention

When it happens

Trigger: Calling getGame(key) with any string that is not one of 'prisoners-dilemma', 'pd', 'match-or-not', or 'mon'. Examples: getGame('prisoner-dilemma') (typo, missing 's'), getGame('rock-paper-scissors') (unregistered game), getGame('PD') (case-sensitive miss), getGame('') (empty).

Common situations: CLI flag or config value with a typo or wrong casing; reusing the canonical name when only the alias is registered (or vice versa); adding a new game spec but forgetting to register it in the GAMES object; user input from an HTTP/form field passed straight into getGame without an allowlist check.

Related errors


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