Skip to content

Kilocode CLI Race Condition #4541

@ActaVerba

Description

@ActaVerba

Plugin Type

Kilo Code CLI

App Version

CLI version: 0.17.0

Description

GitHub Issue: kilocode CLI Race Condition

Repository: https://github.com/Kilo-Org/kilocode/issues/new

Title: CLI: Race condition corrupts config.json when multiple instances run in parallel


Bug Description

When running multiple kilocode CLI instances in parallel (e.g., via a wrapper script that spawns multiple agents), the config.json file can become corrupted with duplicate closing braces, resulting in invalid JSON.

Steps to Reproduce

  1. Run multiple kilocode CLI instances in parallel targeting the same config:
# Using a wrapper like ask-agent that spawns multiple kilocode instances
kilocode --model x-ai/grok-code-fast-1 "task" &
kilocode --model minimax/minimax-m2:free "task" &
kilocode --model mistralai/devstral-2512:free "task" &
wait
  1. After several parallel runs, ~/.kilocode/cli/config.json may become corrupted:
{
  "version": "1.0.0",
  ...
  "customThemes": {}
}
}   // <-- Extra closing brace causes JSON parse error

Expected Behavior

Config file should remain valid JSON regardless of how many parallel instances run.

Actual Behavior

Config file becomes corrupted with duplicate closing braces, causing subsequent runs to fail with:

SyntaxError: Unexpected non-whitespace character after JSON at position 1430

Root Cause Analysis

In cli/src/config/persistence.ts:

  1. loadConfig() (line ~169) always calls saveConfig() after loading to persist merged defaults
  2. saveConfig() uses fs.writeFile() without any file locking
  3. When multiple instances run in parallel, they race to write the config
  4. Simultaneous writes can result in partial/duplicate content
// persistence.ts line 169-173
// Save the merged config back to ensure all defaults are persisted
await saveConfig(config)

// persistence.ts saveConfig()
await fs.writeFile(configFile, JSON.stringify(config, null, 2))  // No locking!

Suggested Fix

Add file locking around config read/write operations using a package like proper-lockfile:

import lockfile from 'proper-lockfile';

export async function saveConfig(config: CLIConfig): Promise<void> {
  if (isEphemeralMode()) return;

  await ensureConfigDir();

  // Acquire lock before writing
  const release = await lockfile.lock(configFile, { retries: 3 });
  try {
    await fs.writeFile(configFile, JSON.stringify(config, null, 2));
  } finally {
    await release();
  }
}

Alternatively, consider:

  • Not writing config on every load (only when explicitly changed)
  • Using atomic writes (write to temp file, then rename)
  • Per-session config isolation for parallel mode

Environment

  • OS: Windows 11
  • kilocode CLI version: 0.17.0
  • Node.js: v22.19.0

Workaround

Current workaround is to:

  1. Git-track the config file
  2. Run repair script after parallel operations:
# repair_config.py
import json, subprocess
from pathlib import Path

config = Path.home() / ".kilocode/cli/config.json"
try:
    json.loads(config.read_text())
except json.JSONDecodeError:
    subprocess.run(["git", "checkout", "config.json"], cwd=config.parent)

Labels: bug, cli

Reproduction steps

Steps to Reproduce

  1. Run multiple kilocode CLI instances in parallel targeting the same config:
# Using a wrapper like ask-agent that spawns multiple kilocode instances
kilocode --model x-ai/grok-code-fast-1 "task" &
kilocode --model minimax/minimax-m2:free "task" &
kilocode --model mistralai/devstral-2512:free "task" &
wait
  1. After several parallel runs, ~/.kilocode/cli/config.json may become corrupted:
{
  "version": "1.0.0",
  ...
  "customThemes": {}
}
}   // <-- Extra closing brace causes JSON parse error

Provider

Multiple free providers

Model

x-ai/grok-code-fast-1, minimax/minimax-m2:free, mistralai/devstral-2512:free

System Information

OS: Windows 11
kilocode CLI version: 0.17.0
Node.js: v22.19.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    Status

    Intake

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions