[BUG] Regression in 2.1.259: managed-settings discovery on WSL fails fatally when /mnt/c is inaccessible (works in 2.1.258)

Status Open
Reported on v2.1.259
Maintainer reply None cached
Activity 4 comments · opened Sep 3, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

On WSL2, with Claude Code installed inside the distribution and never installed
on the Windows side, 2.1.259 fails when the WSL user has no access to /mnt/c.
2.1.258 works correctly on the same machine with the same configuration.

Claude Code probes the Windows managed-settings sources on WSL to determine
whether wslInheritsWindowsSettings is set (the flag is honored only from HKLM or
a file under C:\Program Files\ClaudeCode, so the probe is necessarily
unconditional). On this host that probe returns EACCES rather than ENOENT,
because the user deliberately has no access to the Windows filesystem.

An EACCES on traversal delivers no policy key, exactly as ENOENT delivers none.
Inheritance is off either way and /etc/claude-code is the correct next source.
2.1.258 evidently handled it that way. 2.1.259 appears to treat the inaccessible
path as an unreadable managed policy and fails closed.

Verified on the Windows side by a Windows administrator — no managed source
exists in any device-local location:

  • HKLM\SOFTWARE\Policies\ClaudeCode -> no Settings value
  • HKCU\SOFTWARE\Policies\ClaudeCode -> no Settings value
  • C:\Program Files\ClaudeCode -> does not exist (Test-Path False)

So the failure occurs while resolving a policy that verifiably cannot exist.

Notably, claude --version succeeds on 2.1.259; only sessions fail. That should
narrow where in startup the fatality sits.

No sandbox is involved: strace shows the failing syscalls in the main node
process, with no bwrap execve. CLAUDE_CODE_SUBPROCESS_ENV_SCRUB is not set.
This is distinct from #80212, which is a bwrap bind-mount failure on the same
path.

What Should Happen?

An inaccessible drvfs path should be treated as an absent managed source, not as
an unreadable policy. EACCES on traversal yields no policy key, so WSL
inheritance stays off and Claude Code should proceed to /etc/claude-code and
start the session normally — as 2.1.258 does.

This costs no enforcement guarantee: a machine where the Windows sources are
unreachable cannot have had an admin-only flag read from them under any version.

Error Messages/Logs

strace -f -e trace=openat,stat,statx,mount,mkdir,clone,execve, main process:

statx(AT_FDCWD, "/mnt/c/Program Files", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, 0x7fffa41e94f8) = -1 EACCES (Permission denied)
openat(AT_FDCWD, "/mnt/c/Program Files", O_RDONLY|O_LARGEFILE|O_NOFOLLOW|O_DIRECTORY) = -1 EACCES (Permission denied)
openat(AT_FDCWD, "/mnt/c/Program Files/ClaudeCode/managed-settings.json", O_RDONLY|O_LARGEFILE|O_PATH) = -1 EACCES (Permission denied)
statx(AT_FDCWD, "/mnt/c/Program Files/ClaudeCode/managed-settings.json", AT_STATX_SYNC_AS_STAT, STATX_ALL, 0x7fffa41e9548) = -1 EACCES (Permission denied)
openat(AT_FDCWD, "/mnt/c/Program Files/ClaudeCode/managed-settings.d", O_RDONLY|O_LARGEFILE|O_DIRECTORY) = -1 EACCES (Permission denied)

Note the first denial is on "/mnt/c/Program Files" itself — one level above the
directory being looked for.

User-facing error on 2.1.259:
Unable to read managed policy settings.
This machine may require organization login enforcement, but the policy file failed to load.
Contact your administrator.

Detail: /mnt/c/Program Files/ClaudeCode/managed-settings.json: Settings file could not be read: EACCES: permission denied, stat '/mnt/c/Program Files/ClaudeCode/managed-settings.json'

Steps to Reproduce

  1. WSL2 distribution with Claude Code installed inside it (Linux installer).

Claude Code NOT installed on the Windows side; C:\Program Files\ClaudeCode
does not exist.

  1. Configure the WSL user to have no access to /mnt/c (automount mask in

/etc/wsl.conf). Confirm: statx on "/mnt/c/Program Files" returns EACCES.

  1. Confirm no Windows managed source exists (HKLM, HKCU, file-based — all empty).
  2. Run 2.1.258 -> works.
  3. Run 2.1.259 -> <failure>.

Claude Model

Not sure / Multiple models

Is this a regression?

Yes, this worked in a previous version

Last Working Version

2.1.258

Claude Code Version

2.1.259

Platform

Anthropic API

Operating System

Other Linux

Terminal/Shell

WSL (Windows Subsystem for Linux)

Additional Information

_No response_

View original on GitHub ↗

3 Comments

namazso · 26 days ago

Workaround, written by GPT 6 Astra:

'use strict';

/*
 * Claude Code 2.1.277 WSL policy-discovery workaround.
 *
 * Fr() probes Windows managed-settings.json and managed-settings.d to discover
 * wslInheritsWindowsSettings. EACCES becomes a fatal admin-policy error even
 * when inheritance was never enabled and the entire Windows mount is private
 * to another user. Both probes must be handled; fixing just stat is not enough.
 *
 * Usage:
 *   BUN_OPTIONS="--preload=/absolute/path/claude-wsl-policy-preload.cjs" claude
 *
 * This native executable supports Bun preloads. Its direct openat syscalls
 * bypass ordinary libc LD_PRELOAD hooks, so patch the actual fs API instead.
 * No global environment, mount permissions, or installed binary is modified.
 *
 * Use only where Windows policy inheritance is not required: an inaccessible
 * Windows policy cannot be inspected to establish whether it was intended.
 * Linux policy and accessible Windows policy keep their normal semantics.
 */
const fs = require('node:fs');
const { constants: { errno } } = require('node:os');
const { syncBuiltinESMExports } = require('node:module');

const mount = '/mnt/c';
const policyFile = '/mnt/c/Program Files/ClaudeCode/managed-settings.json';
const policyDirectory = '/mnt/c/Program Files/ClaudeCode/managed-settings.d';
const accessSync = fs.accessSync;

function isInaccessibleDiscovery(path, error) {
  if (error?.code !== 'EACCES' ||
      (path !== policyFile && path !== policyDirectory)) {
    return false;
  }
  // Check at the time of failure, not once at preload time. Never hide a
  // file-specific permission error on an otherwise traversable Windows mount.
  try {
    accessSync(mount, fs.constants.X_OK);
    return false;
  } catch (mountError) {
    return mountError?.code === 'EACCES';
  }
}

// These are the synchronous APIs used by the installed discovery implementation.
// Keep all overloads/options, receivers, successful results, and other errors.
for (const name of ['statSync', 'readFileSync', 'readdirSync']) {
  const original = fs[name];
  fs[name] = function () {
    try {
      return Reflect.apply(original, this, arguments);
    } catch (error) {
      const path = arguments[0];
      if (!isInaccessibleDiscovery(path, error)) throw error;
      throw Object.assign(new Error(
        `ENOENT: no such file or directory, ${error.syscall} '${path}'`,
        { cause: error },
      ), {
        code: 'ENOENT',
        errno: -errno.ENOENT,
        syscall: error.syscall,
        path,
      });
    }
  };
}

// Node named imports must see the same methods as require('fs'). Bun also
// exposes this API, so the module can be regression-tested with either runtime.
syncBuiltinESMExports();
OliverF · 25 days ago

Still present on 2.1.275.

Setup:

I have a slightly different setup with the same end result: Instead of masking the mount, I run Claude Code as a dedicated unprivileged Unix account with no traverse permission on /mnt. The fact the same error occurs suggests the trigger is any EACCES on that traversal rather than anything specific to the automount mask.

I have no C:\Program Files\ClaudeCode in Windows, and no Settings value under HKLM\SOFTWARE\Policies\ClaudeCode or HKCU\SOFTWARE\Policies\ClaudeCode.

On the same WSL2 machine using a standard account with access to /mnt, running claude produces no warning at all, presumably because claude sees the path is absent entirely and correctly continues.

Partial workaround:

Creating a non-empty /etc/claude-code/managed-settings.json downgrades the fatal exit to a prompt each time claude is started. Any setting seems to work - I tested with {"spinnerTipsEnabled": true}, {"cleanupPeriodDays": 30}, and {"permissions": {"deny": ["Read(//mnt/**)"]}}. Notably, {} does NOT work, so it does need a valid key.

Here's the error on startup with /etc/claude-code/managed-settings.json populated with {"spinnerTipsEnabled": true}:

Settings Error

  /mnt/c/Program Files/ClaudeCode/managed-settings.d
  └ Managed settings drop-in directory could not be read: EACCES: permission denied, scandir '/mnt/c/Program Files/ClaudeCode/managed-settings.d'
  /mnt/c/Program Files/ClaudeCode/managed-settings.json
  └ Settings file could not be read: EACCES: permission denied, stat '/mnt/c/Program Files/ClaudeCode/managed-settings.json'

  Files with errors are skipped entirely, not just the invalid settings.

    1. Fix with Claude
    2. Exit and fix manually
    3. Continue without these settings

Choosing 3 starts the session. But Claude still treats it as an error even when a local admin source supplies a policy, so this happens on each start.

Version: 2.1.275
Login: claude.ai subscription.
Platform: WSL2, Debian, Claude Code installed inside the distribution via the Linux installer

hamfield-eu · 22 days ago

Same issue for me. Downgrading to 2.1.258 fixed it. Only difference for me was that I had Claude Code installed on Windows itself.

Showing cached comments. Read the full discussion on GitHub ↗