[BUG] policyHelper in /etc/claude-code/managed-settings.json is ignored on Linux when org server-managed (remote) settings are present
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?
Per the docs, policyHelper configured in a system managed-settings.json should be executed at startup and its output used as the managed configuration for the run. On Linux, when the org's server-managed (remote) settings are present, the policyHelper executable is never invoked and the endpoint-managed file is ignored entirely; the run silently uses the cached remote settings instead.
What Should Happen?
Helper is executed, the emitted managed settings apply.
Error Messages/Logs
(none)
Steps to Reproduce
- Org account with server-managed settings (remote cache exists at ~/.claude/remote-settings.json).
- Create an executable helper that appends an invocation log and prints a valid managed-settings JSON envelope on stdout, e.g.:
{"managedSettings":{"hooks":{"SessionStart":[{"matcher":"*","hooks":[{"type":"command","command":"touch /tmp/applied.marker"}]}]}}}
- Verify standalone: running the helper prints the JSON and exits 0.
- Set /etc/claude-code/managed-settings.json to { "policyHelper": { "path": "/opt/bone-policy-probe.sh" } }.
- Run claude -p "reply: ok" --debug-file /tmp/cc-debug.log.
Claude Model
_No response_
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.197
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Terminal.app (macOS)
Additional Information
Claude is running in a linux container on Mac host.
Showing cached comments. Read the full discussion on GitHub ↗
4 Comments
Found 1 possible duplicate issue:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Confirming on macOS (Darwin 25.5.0), Claude Code v2.1.205, with one
diagnostically significant difference from the original report: on this
platform/version the helper is executed — but its output is then
silently discarded in favor of the cached server-managed payload.
Evidence, using a canary methodology:
/Library/Application Support/ClaudeCode/managed-settings.json:``
json
``{
"policyHelper": {
"path": "/Library/Application Support/ClaudeCode/policy-helper.sh",
"timeoutMs": 10000,
"refreshIntervalMs": 0
}
}
deny rule:
``
json
``{ "managedSettings": { "permissions": { "deny": ["Bash(policyhelper-canary-d34db33f *)"] } } }
claude --debug-file /tmp/cc.log -p "ok"), then:— it ran (this differs from the original report's "never invoked" on
Linux/2.1.197; possibly version drift in where it dies).
grep canary /tmp/cc.log→ nothing. The session'sApplying permission update ... destination 'policySettings'linescontain only the org's server-delivered rules; the canary never
appears anywhere.
grep -i helper /tmp/cc.log→ zero matches. No error, no validationwarning, no "output rejected" — the discard is completely silent.
and the canary is present in stdout, so the envelope is well-formed.
Two adjacent observations from the same test matrix, in case they share a
root cause:
policyHelpervalue (bare path string instead of theobject form), the key is stripped silently and the helper never runs —
expected fail-safe behavior, noted for contrast: the bug above occurs
with the valid object form.
managed-settings.d/, or helper output) never fire on this version whilemanaged permission rules do apply — same lineage as #33824.
Impact framing: administrators following the documented precedence
("
policyHelperoutput: when configured, this is the only managed sourceused") may believe posture-based policy is in force when it is not, with no
signal. Meanwhile the configured helper still executes at every startup, so
deployments get the feature's code-execution surface without its policy
effect. Suggested: implement the documented precedence, or emit an explicit
diagnostic when a configured helper's output is not applied, and gate the
docs section with the version where the precedence takes effect.
Re the duplicate flag: not a strict duplicate of #70181 — that issue concerns an empty remote payload (304-cached) zeroing out endpoint-file deny/allow rules, contradicting the documented "first source that delivers a non-empty configuration" rule. This issue is a different rung of the same documented ladder:
policyHelperis documented to preempt the remote payload outright, yet its output is not applied — on macOS/v2.1.205 the helper executes and its well-formed output is silently discarded (see canary reproduction above). Likely the same root-cause family (remote cache unconditionally winning regardless of the documented precedence), so cross-linking rather than merging preserves both reproductions.Confirming on macOS 15 (Darwin 25.5.0, arm64), Claude Code v2.1.223 and on
Windows, with a behavioural difference from the v2.1.205 report above: on both of
these the helper is never executed at all, matching the original Linux/v2.1.197
report rather than the runs-then-discards behaviour seen at v2.1.205.
macOS — method and result
Helper at
/usr/local/bin/claude-policy,root:wheel 0755, instrumented to append atimestamp to a marker file on every invocation:
Referenced from root-owned
/Library/Application Support/ClaudeCode/managed-settings.json:| Check | Result |
| :--- | :--- |
| Helper standalone | ✅ one line of stdout, parses as JSON, exit 0 |
|
managed-settings.jsonvalid | ✅jqparses || Claude Code sees the file | ✅
Watching for changes in setting files ~/.claude/settings.json, /Library/Application Support/ClaudeCode/managed-settings.json...|| MDM load |
MDM settings load completed in 1ms|| Marker file after
claude doctor| ❌ never created || Marker file after full session (
claude --debug-file … -p "…") | ❌ never created || Remote settings | still applied —
Remote settings: Using cached settings (304)|grep -i 'policy\|helper'over the debug log produces no reference to the configuredhelper — no invocation, no error, no validation warning. Silent.
Org context: an account with server-managed settings configured, so the remote cache
at
~/.claude/remote-settings.jsonis populated.Windows
Same construction via
C:\Program Files\ClaudeCode\managed-settings.jsonpointing at a.cmdhelper./permissionscontinued to report bypass permissions as "disabled bysettings", i.e. the server-delivered
disableBypassPermissionsModeremained in forceand the helper's output never applied.
Because a plain POSIX
shhelper reproduces the same negative result on macOS, this isnot a
.cmd-vs-.exeexecutability problem.Two notes for triage
platform:linuxlabel looks too narrow. Reproduces on macOS and Windows.Across the reports the failure point drifts by version — never-invoked at 2.1.197 and
2.1.223, runs-then-output-discarded at 2.1.205 — but the net effect is identical.
a small set of keys honoured "when any admin-controlled managed source sets them"
(the sandbox allowlist locks,
allowAllClaudeAiMcps, the sandbox binary paths,forceRemoteSettingsRefresh, and — as of v2.1.223 — theenvblock merging per key).Each of those looks like this same defect fixed for one key.
policyHelperisdocumented as preempting every managed source, yet is not on that list, which fits
the hypothesis in #70181 that the remote payload unconditionally wins whenever it
exists.
Requests
configured helper's output is not applied. Silent discard means administrators believe
posture-based policy is in force when it is not.
present, and an empty/304 remote response zeroes out local managed rules when absent,
then endpoint-managed settings have no reliable configuration at all for an org that
uses the admin console. That combination is the blocking problem, more than either
issue alone.