AI Traces collection known Claude Code issues with remote settings

Last updated: August 28, 2026

This page documents Claude Code behavior we have reproduced that might affect Span's coding hooks on managed (MDM) installations, along with the workarounds
available today.

Remote settings replace managed hooks on Windows

Summary. On Windows, publishing any remote (organization) settings for Claude Code causes the hooks installed in Claude Code's managed settings drop-in to stop firing. On macOS the same remote settings merge with the drop-in and hooks keep working. The behavior is inconsistent between the two platforms.

Span installs its hooks as a dedicated, Span-owned file alongside your own managed settings, so it never modifies your configuration:

  • Windows: %ProgramFiles%\ClaudeCode\managed-settings.d\span-hooks.json

  • macOS: /Library/Application Support/ClaudeCode/managed-settings.d/span-hooks.json

On Windows, once remote settings are published for the organization, the hooks in that file are no longer executed. This happens regardless of what the remote settings contain — an otherwise empty policy is enough to trigger it. The drop-in file is still on disk and still valid; Claude Code simply stops running the hooks it defines.

Symptoms

A device in this state looks healthy in every respect except that no coding activity is ever recorded:

  • Span receives heartbeats from the device but no trace data, while the same users are demonstrably active (commits and pull requests still show up under tool utilization).

  • span-health reports the delivery pipeline as healthy — dispatcher installed, auth token valid, collector running and accepting events — with delivery_log: no log yet. That last line is the tell: it means the Span hook dispatcher has never been invoked at all, rather than having run and failed to deliver.

  • Restarting the machine does not help, because nothing is broken on the device.

Confirming it

Run this as the signed-in user on an affected device:

& "$env:LOCALAPPDATA\Span\bin\hook-dispatcher.exe" span-health
Get-Content "$env:USERPROFILE\.claude\remote-settings.json"

If delivery_log reads no log yet and the second command returns a policy (anything other than the empty {} placeholder), you are seeing this issue.

Workarounds

Either of the following restores trace collection. They can be used independently; pick the one that fits how you manage devices.

Workaround 1 — Define the hooks in remote settings

This is managed under https://claude.ai/admin-settings/claude-code, by admin users.

Because remote settings take precedence, defining the hooks there puts them back in effect. Add the hooks block below to your existing Claude Code remote settings, keeping the rest of your policy unchanged.

Every command is guarded by a Test-Path check, so on devices where the Span dispatcher is not installed the hook does nothing and no error is raised. The trailing exit 0 guarantees a hook can never interfere with a user's session.

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "if (Test-Path \"$env:LOCALAPPDATA\\Span\\bin\\hook-dispatcher.exe\") { & \"$env:LOCALAPPDATA\\Span\\bin\\hook-dispatcher.exe\" claude userPromptSubmitEvents }; exit 0",
            "shell": "powershell"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "if (Test-Path \"$env:LOCALAPPDATA\\Span\\bin\\hook-dispatcher.exe\") { & \"$env:LOCALAPPDATA\\Span\\bin\\hook-dispatcher.exe\" claude preToolUseEvents }; exit 0",
            "shell": "powershell"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "if (Test-Path \"$env:LOCALAPPDATA\\Span\\bin\\hook-dispatcher.exe\") { & \"$env:LOCALAPPDATA\\Span\\bin\\hook-dispatcher.exe\" claude postToolUseEvents }; exit 0",
            "shell": "powershell"
          }
        ]
      }
    ],
    "PostToolUseFailure": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "if (Test-Path \"$env:LOCALAPPDATA\\Span\\bin\\hook-dispatcher.exe\") { & \"$env:LOCALAPPDATA\\Span\\bin\\hook-dispatcher.exe\" claude postToolUseFailureEvents }; exit 0",
            "shell": "powershell"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "if (Test-Path \"$env:LOCALAPPDATA\\Span\\bin\\hook-dispatcher.exe\") { & \"$env:LOCALAPPDATA\\Span\\bin\\hook-dispatcher.exe\" claude stopEventsEnhanced }; exit 0",
            "shell": "powershell"
          }
        ]
      }
    ],
    "SessionEnd": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "if (Test-Path \"$env:LOCALAPPDATA\\Span\\bin\\hook-dispatcher.exe\") { & \"$env:LOCALAPPDATA\\Span\\bin\\hook-dispatcher.exe\" claude sessionEndEvents }; exit 0",
            "shell": "powershell"
          }
        ]
      }
    ],
    "PreCompact": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "if (Test-Path \"$env:LOCALAPPDATA\\Span\\bin\\hook-dispatcher.exe\") { & \"$env:LOCALAPPDATA\\Span\\bin\\hook-dispatcher.exe\" claude preCompactEventsEnhanced }; exit 0",
            "shell": "powershell"
          }
        ]
      }
    ],
    "SubagentStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "if (Test-Path \"$env:LOCALAPPDATA\\Span\\bin\\hook-dispatcher.exe\") { & \"$env:LOCALAPPDATA\\Span\\bin\\hook-dispatcher.exe\" claude subagentStartEvents }; exit 0",
            "shell": "powershell"
          }
        ]
      }
    ],
    "SubagentStop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "if (Test-Path \"$env:LOCALAPPDATA\\Span\\bin\\hook-dispatcher.exe\") { & \"$env:LOCALAPPDATA\\Span\\bin\\hook-dispatcher.exe\" claude subagentStopEvents }; exit 0",
            "shell": "powershell"
          }
        ]
      }
    ]
  }
}

Warning — users will be prompted to approve these settings. Claude Code treats hooks delivered through remote settings as security-sensitive, and each user is shown a "Managed settings require approval" dialog listing hooks the first time the policy reaches them. They must choose "Yes, I trust these settings" before the hooks take effect; choosing "No" exits Claude Code. The prompt appears once per organization policy — publishing a changed policy later prompts again. Tell your users to expect it, or they are likely to decline. Hooks installed from the on-disk managed drop-in, and those merged into user settings by Workaround 2, are not subject to this prompt.

Pros: no per-device setup, applies immediately and everywhere, and nothing is written into user profiles. Safe to publish to a mixed fleet.

Cons: every user is prompted to approve the settings, as described above, and the policy applies organization-wide — including devices that are not part of your AI trace tracking at all. There is no way to scope remote settings to a subset of users or machines.

The hooks themselves are safe to publish organization-wide: each one declares "shell": "powershell", so macOS and Linux devices ignore them.

Workaround 2 — Copy the managed hooks into user settings via MDM

Run a script from your MDM that reads the Span-owned managed drop-in and merges its hooks into the user's own %USERPROFILE%\.claude\settings.json, which is still honored on Windows. The full script is at the end of this section.

The merge is additive and idempotent: existing hooks from other tools are left untouched, Span hooks that are already present are not duplicated, and stale Span hooks left over from earlier versions are removed. Run it on a schedule so the user settings stay in step with the managed drop-in across upgrades.

Pros: less invasive, and it can be targeted at specific users or device groups rather than the entire organization. It keeps the configuration Windows-only by construction, and it raises no approval prompt for users.

Cons: it requires additional MDM setup and ongoing scheduling, and it writes Span configuration into user-level settings, which is not where a managed installation should normally place it. Users can edit or delete that file themselves, so the script needs to run repeatedly rather than once.

The script

Download Merge-SpanHooks.ps1 and run it from your MDM as the signed-in user. It needs no administrator rights, since it only writes inside the user's own profile:

powershell -ExecutionPolicy Bypass -File .\Merge-SpanHooks.ps1

By default it reads the managed drop-in from %ProgramFiles%\ClaudeCode\managed-settings.d\span-hooks.json and the user settings from %USERPROFILE%\.claude\settings.json; both can be overridden with -ManagedFile and -SettingsFile. A timestamped .bak copy is written before any change.

Settings that can conflict with Span's hooks

The settings below are unrelated to the Windows issue above, but each one can stop Span's hooks from running. If traces stop arriving after a policy change, check these first.

disableAllHooks

Disables all hooks and status line execution, Span's included. When set to true in managed settings, users and projects cannot override it. There is no exemption for managed or vendor hooks — if this is on, Span collects nothing.

allowManagedHooksOnly

Managed settings only. Prevents user, project, and plugin hooks from loading; only managed and SDK hooks run.

This does not affect a standard Span installation. Span installs its hooks into Claude Code's managed settings drop-in (managed-settings.d/span-hooks.json), so they count as managed hooks and continue to run normally with this setting enabled.

It does, however, break Workaround 2 above, which works by copying those hooks into the user's own settings.json — user-level hooks are exactly what this setting blocks. If you rely on that workaround, leave allowManagedHooksOnly unset, or use Workaround 1 instead.

strictPluginOnlyCustomization

Managed settings. Blocks non-plugin customization sources for the surfaces you list. If the value is true, or an array containing "hooks", only plugin-provided hooks are allowed and Span's hooks will not run.

Settings that are safe

allowedHttpHookUrls and httpHookAllowedEnvVars apply only to HTTP hooks. Span installs command hooks, which these settings do not affect — including "allowedHttpHookUrls": [], which blocks all HTTP hooks but leaves Span working normally.