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

  1. 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
  2. Or append the section manually to ~/.codex/config.toml before calling
  3. 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

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


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