ruvnet/ruflo · error · Error
unknown game " ". Known
Error message
unknown game "${key}". Known: ${Object.keys(GAMES).join(', ')} What it means
The config MCP tools (config_get / config_save) run every user-supplied path through validateConfigPath(), a security hardening step that refuses any path whose normalized form starts with '/' or '\'. The tool only accepts config paths relative to the MCP server's process.cwd(), preventing an MCP client from reading or writing arbitrary host files. Even a perfectly valid absolute path like /home/me/proj/claude-flow.config.json is rejected before any other check runs.
Solutions
- Pass a relative path rooted at the MCP server's working directory, e.g. "./claude-flow.config.json" or "config/prod.config.json"
- If you hold an absolute path, convert it first: path.relative(process.cwd(), absPath) and pass the result (keep it free of '..' segments)
- Start the MCP server with its working directory set to the project root so the default './claude-flow.config.json' resolves correctly
- If absolute paths are a hard requirement, modify validateConfigPath to accept an explicit allowlisted base directory instead of relying on cwd
Example fix
// before
await client.callTool('config_save', {
path: path.resolve('./config/claude-flow.config.json'), // -> /abs/... throws [1120]
config: cfg,
});
// after
await client.callTool('config_save', {
path: 'config/claude-flow.config.json', // relative to server cwd
config: cfg,
}); Defensive patterns
Strategy: validation
Validate before calling
import { isAbsolute, relative, normalize } from 'path';
function toSafeConfigPath(raw: string): string | null {
const rel = isAbsolute(raw) ? relative(process.cwd(), raw) : normalize(raw);
if (isAbsolute(rel) || rel.startsWith('..')) return null; // cannot be expressed safely
return rel.split(/[\\/]/).includes('..') ? null : rel;
} Try / catch
try {
await client.callTool('config_save', { path, config });
} catch (e) {
if (e instanceof Error && e.message.includes('Absolute paths are not allowed')) {
path = toSafeConfigPath(path) ?? './claude-flow.config.json';
await client.callTool('config_save', { path, config });
} else throw e;
} Prevention
- Standardize on relative config paths everywhere in client code; never path.resolve() before sending
- Centralize config-path construction in one helper that enforces relative + no '..'
- Run MCP servers from a stable project directory so './claude-flow.config.json' is always correct
When it happens
Trigger: Calling config_save or config_load with path="/home/user/project/claude-flow.config.json"; passing a Windows drive-absolute path such as "C:\projects\config.json" (its normalize()d form still starts with the drive, but UNC paths \\server\share\c.json start with '\' and hit this branch); a client wrapper that does path.resolve() or path.join(rootDir, ...) before sending the tool call.
Common situations: Scripts ported from the claude-flow CLI (which accepts absolute config paths) to the MCP tool API; CI pipelines that build absolute paths from $PWD or $HOME; MCP clients that 'helpfully' canonicalize relative paths to absolute before invoking the tool.
Related errors
- Dangerous key segment rejected
- signBacktestArtifact: privateKey must be 32 bytes
- unknown strategy " ". Available
- Event log path contains null bytes
- extracted binary path failed validation
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/8d0f5081c1b9e51b.
Report an issue: GitHub.
Appendix: 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 fa13ee4ad6)