Skip to content

writeTextFileAtomic (sync) crashes with EPERM on Docker volumes mounted from Windows #53947

@StbanMc

Description

@StbanMc

Bug description

The synchronous writeTextFileAtomic function calls fs.chmodSync() without a try/catch, which throws EPERM: operation not permitted, chmod when running inside a Docker container with a volume mounted from a Windows host (e.g., via Docker Desktop with WSL2).

This prevents the gateway from starting or writing config/auth files on Windows+Docker setups.

Environment

  • Host OS: Windows 11 (Docker Desktop with WSL2)
  • Container: openclaw:local (Node.js, Linux)
  • Version: 2026.3.11
  • Volume mount: Windows directory → /home/node/.openclaw

Root cause

The async version of the file writer correctly wraps chmod in try/catch:

// Async version (correct) — handles EPERM gracefully
try {
    await fs.chmod(tmp, mode);
} catch {}
await fs.rename(tmp, filePath);
try {
    await fs.chmod(filePath, mode);
} catch {}

But the sync version does not:

// Sync version (broken) — throws on Docker/Windows volumes
function writeTextFileAtomic(pathname, value, mode = 384) {
    ensureDirForFile(pathname);
    const tempPath = `${pathname}.tmp-${process.pid}-${Date.now()}`;
    fs.writeFileSync(tempPath, value, "utf8");
    fs.chmodSync(tempPath, mode);   // ← EPERM here
    fs.renameSync(tempPath, pathname);
}

On Docker volumes mounted from Windows (NTFS/CIFS), chmod is not supported and throws EPERM. The async version already handles this — the sync version should too.

Error output

EPERM: operation not permitted, chmod '/home/node/.openclaw/agents/main/agent/auth-profiles.json'

Suggested fix

Wrap fs.chmodSync in a try/catch, consistent with the async version:

function writeTextFileAtomic(pathname, value, mode = 384) {
    ensureDirForFile(pathname);
    const tempPath = `${pathname}.tmp-${process.pid}-${Date.now()}`;
    fs.writeFileSync(tempPath, value, "utf8");
    try { fs.chmodSync(tempPath, mode); } catch {}
    fs.renameSync(tempPath, pathname);
}

Impact

  • Blocks all Windows + Docker Desktop users from running the gateway
  • Affects any auth/config file write that uses the sync path
  • Easy fix, already solved in the async counterpart

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