ruvnet/ruflo · error · Error
${header} not found in Codex config
Error message
${header} not found in Codex config What it means
upsertMcpServerStartupTimeout() performs a surgical text edit of the Codex config.toml: it locates the literal header line `[mcp_servers.ruflo]` (trim-matched) and rewrites the section body. If the section does not exist, the function refuses to invent it and throws '<header> not found in Codex config' — it is an editor for an existing section, not a creator.
Source
Thrown at v3/@claude-flow/codex/src/mcp-config.ts:142
}
export function hasExpectedRufloMcpTimeout(registration: CodexMcpRegistration): boolean {
return typeof registration.startup_timeout_sec === 'number'
&& registration.startup_timeout_sec >= RUFLO_MCP_STARTUP_TIMEOUT_SEC;
}
export function upsertMcpServerStartupTimeout(
config: string,
serverName = RUFLO_MCP_SERVER_NAME,
timeoutSec = RUFLO_MCP_STARTUP_TIMEOUT_SEC,
): string {
const eol = config.includes('\r\n') ? '\r\n' : '\n';
const lines = config.split(/\r?\n/);
const header = `[mcp_servers.${serverName}]`;
const start = lines.findIndex(line => line.trim() === header);
if (start < 0) {
throw new Error(`${header} not found in Codex config`);
}
let end = lines.length;
for (let index = start + 1; index < lines.length; index += 1) {
if (lines[index]?.trim().startsWith('[')) {
end = index;
break;
}
}
const timeoutPattern = /^\s*startup_timeout_sec\s*=/;
for (let index = start + 1; index < end; index += 1) {
const line = lines[index] ?? '';
if (timeoutPattern.test(line)) {
const parsed = line.match(/^(\s*startup_timeout_sec\s*=\s*)([0-9][0-9_]*)(.*)$/);
const currentValue = parsed ? Number(parsed[2]!.replace(/_/g, '')) : Number.NaN;
if (Number.isFinite(currentValue) && currentValue >= timeoutSec) {
return config;View on GitHub (pinned to fa13ee4ad6)
Solutions
- Register the server first (e.g. `codex mcp add ruflo -- npx ruflo mcp start` or the project's setup command) so the section exists, then call the upsert
- Or append the section manually to ~/.codex/config.toml before calling
- Pass the matching serverName if the server was registered under a different key
Example fix
# before: ~/.codex/config.toml has no ruflo section # after: add it, then re-run [mcp_servers.ruflo] command = "npx" args = ["ruflo", "mcp", "start"] startup_timeout_sec = 120
Defensive patterns
Strategy: validation
Validate before calling
function hasSection(config: string, serverName: string): boolean {
return config.split(/\r?\n/).some(l => l.trim() === `[mcp_servers.${serverName}]`);
} Try / catch
let cfg = readConfig(); if (!hasSection(cfg, 'ruflo')) cfg += '\n[mcp_servers.ruflo]\ncommand = "npx"\nargs = ["ruflo", "mcp", "start"]\n'; writeConfig(cfg); cfg = upsertMcpServerStartupTimeout(cfg);
Prevention
- Run registration/setup before any config-patching step; order matters
- Key edits by the same serverName used at registration
- Keep backups of config.toml before programmatic edits
When it happens
Trigger: Calling upsertMcpServerStartupTimeout on a fresh ~/.codex/config.toml that has no [mcp_servers.ruflo] section; the server was registered under a different name (serverName mismatch); manual edits removed or renamed the header; CRLF/BOM formatting shifted the line so trim() still matches only if truly present.
Common situations: Setup steps run out of order (timeout upsert before `codex mcp add` created the section); users renamed the MCP server; config regenerated from scratch by another tool.
Related errors
- unknown game "${key}". Known: ${Object.keys(GAMES).join(', '
- unknown strategy "${name}". Available: ${roster.map((r) => r
- localCompute: no adapter for graphId=${input.graphId}
- signBacktestArtifact: privateKey must be 32 bytes (got ${pri
- hexToBytes: odd-length hex string
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/842c1abf3f8a85ff.
Report an issue: GitHub.