Skip to content

[Windows] Plugin update fails with EBUSY: managed plugin directory locked by its own running MCP server child process #2361

Description

@liuj66794-sys

Problem type

Windows file-locking / process-lifecycle bug: the plugin installer cannot remove a managed plugin's directory because that directory is locked by the plugin's own MCP server child process, which kimi-code itself spawned and keeps running during the session.

Error

Error: Failed to install Kimi Datasource: [internal] EBUSY: resource busy or locked, rmdir
'C:\Users\38623\.kimi-code\plugins\managed\kimi-datasource'

Root cause analysis

  1. A managed plugin declares an MCP server whose working directory is the plugin directory itself. Example from ~/.kimi-code/plugins/managed/kimi-datasource/kimi.plugin.json:

    "mcpServers": {
      "data": {
        "command": "node",
        "args": ["./bin/kimi-datasource.mjs"],
        "cwd": "./"
      }
    }
  2. While a kimi-code session is running, the plugin's MCP server stays alive as a child process. Verified on the affected machine:

    kimi.exe  (PID 43740)  __plugin_run_node ./bin/kimi-datasource.mjs
    

    This process's current working directory is C:\Users\38623\.kimi-code\plugins\managed\kimi-datasource, and it also holds an open handle on bin/kimi-datasource.mjs.

  3. On Windows (unlike POSIX), a directory that is any process's working directory — or that contains files with open handles — cannot be removed. fs.rmdir/rm -rf on it fails with EBUSY: resource busy or locked.

  4. When kimi-code tries to install/update the plugin, it removes the existing directory without first stopping the plugin's MCP server child process (or without waiting for it to exit), so the rmdir deterministically fails with EBUSY.

  5. This creates a catch-22 for the user:

    • Keep the session running → the MCP child process holds the lock → update fails with EBUSY.
    • Kill the process/session → the CLI that would perform the update is also gone → update still doesn't happen.

Reproduction

  1. On Windows, install a managed plugin that runs an MCP server (e.g. Kimi Datasource).
  2. Start a kimi-code session (the plugin's MCP server child process starts, with cwd inside the plugin directory).
  3. Trigger a plugin update/reinstall (from /plugins or wherever the update is offered).
  4. Observe: Failed to install ...: [internal] EBUSY: resource busy or locked, rmdir '<plugin dir>'.

Environment

  • OS: Windows 11 (10.0.26200.8875), x86_64
  • kimi-code version: 0.30.0
  • Plugin: kimi-datasource 3.3.0 (managed)
  • Install path: C:\Users\<user>\.kimi-code\plugins\managed\kimi-datasource

Suggested fixes

Any of these would resolve it:

  • Stop-then-reinstall: before removing a managed plugin directory, terminate that plugin's MCP server child process(es) and wait for exit (with a timeout + force kill fallback), then rmdir.
  • Rename-then-swap (more robust on Windows): rename the old directory to a sibling temp name (rename often works even when the dir is busy), install the new version, and delete the old directory on next startup when nothing holds it.
  • Deferred deletion: if rmdir fails with EBUSY/EPERM, schedule the cleanup for next CLI launch instead of hard-failing the whole install.
  • Retry with backoff: Windows AV/indexer locks are often transient; retrying rmdir a few times with delay is a common mitigation — though for this specific bug the lock is held by kimi-code's own child for the whole session, so retries alone won't help without fixing the lifecycle ordering.

Note: the cwd: "./" in the plugin manifest makes the plugin directory itself the MCP server's working directory, which guarantees the lock for the entire session. Even without cwd, the running node process keeps an open handle on bin/kimi-datasource.mjs, which blocks removal of that file on Windows too.

Workaround for affected users

Fully exit all kimi-code sessions, then manually delete ~/.kimi-code/plugins/managed/kimi-datasource, then restart kimi-code and reinstall the plugin. (If a stale kimi.exe ... __plugin_run_node process survives, kill it first.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions