Skip to content

fix(exec-approvals): fs.renameSync EPERM on Windows blocks all exec calls #77785

@MilleniumGenAI

Description

@MilleniumGenAI

Summary

On Windows, every exec call fails with EPERM because writeExecApprovalsRaw in src/infra/exec-approvals.ts uses fs.renameSync(tempPath, targetPath) to atomically replace exec-approvals.json. On Windows, MoveFileExW with MOVEFILE_REPLACE_EXISTING requires DELETE permission on the target file, which fails when any handle (antivirus, search indexer, or transient gateway handle) is open on the target.

Reproduction

  1. Install OpenClaw on Windows (tested on 2026.5.2, 2026.5.3-1, 2026.5.4, Windows 10.0.26200, Node 24.13.1)
  2. Start the gateway
  3. Any exec call produces:
EPERM: operation not permitted, rename 'C:\Users\<user>\.openclaw\.exec-approvals.<PID>.<uuid>.tmp' -> 'C:\Users\<user>\.openclaw\exec-approvals.json'

Root cause

The atomic write pattern (temp + renameSync) added as fix for #54296 is incompatible with Windows. The gateway process holds a transient handle on exec-approvals.json (read during the same exec flow), and renameSync on Windows can't overwrite a file with open handles.

Impact

  • ALL exec calls fail — the approval mechanism itself is the gate that fails
  • gh CLI, shell commands, sub-agents that use exec — all broken
  • Only workaround is direct writeFileSync to the target file (bypassing temp+rename)
  • Reproduced across multiple full gateway restarts and OpenClaw updates

Proposed fix

Add a Windows EPERM fallback in writeExecApprovalsRaw:

function writeExecApprovalsRaw(filePath: string, raw: string) {
  // ... setup unchanged ...
  try {
    fs.writeFileSync(tempPath, raw, { mode: 0o600, flag: "wx" });
    tempWritten = true;
    // Windows: renameSync fails with EPERM when target has open handles
    try {
      fs.renameSync(tempPath, filePath);
    } catch (err) {
      if ((err as NodeJS.ErrnoException).code === "EPERM") {
        fs.copyFileSync(tempPath, filePath);
        fs.unlinkSync(tempPath);
      } else {
        throw err;
      }
    }
  } finally {
    // ... cleanup unchanged ...
  }
}

Environment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions