CLAUDE_CODE_PROCESS_WRAPPER starts every process Claude Code launches from its own binary through your launcher: the background service, every session it hosts in agent view, and Claude Code’s relaunches after an update. Set it to your launcher’s absolute path, and Claude Code runs the launcher with the Claude Code command as its arguments.
A launcher that wraps the claude command on your PATH can’t reach these processes, because they start from the binary’s direct path without looking up claude.
CLAUDE_CODE_PROCESS_WRAPPER requires Claude Code v2.1.208 or later. Earlier versions ignore the variable and start every process unwrapped.What the launcher covers
WithCLAUDE_CODE_PROCESS_WRAPPER set, Claude Code starts each of the following processes through your launcher:
- The background service that
claude agentsand background sessions start on demand. - The terminal host and the Claude Code session inside every agent view row, including the warm standby sessions the service keeps ready.
- Sessions the service respawns after an update or a crash.
- The relaunch Claude Code performs of itself to finish installing an update, including agent view’s restart-for-update action.
exec, which Windows doesn’t support. A Windows machine with the variable set runs every process unwrapped and keeps working, and the only signal is a warning in the debug log. If your launcher policy covers Windows, the variable doesn’t satisfy it there: count Windows machines as unwrapped when you plan the rollout.
Processes that start outside the launcher
Three processes never start through the launcher:- An installed background service:
launchdorsystemdstarts that process from its unit file./statusandclaude daemon statuswarn when this applies, and the sessions the service spawns still start through the launcher once the service restarts with the variable in its settings. - A session you start yourself in a terminal, which runs however you invoked it. To cover these sessions, put a script named
claudein a directory earlier onPATHthat runs your launcher with the real binary; don’t replace the managed symlink. Self-spawns don’t consultPATH, so the two launchers never stack. - The first process of a
claude-cli://deep link, which the operating system’s protocol handler starts directly. Everything that session starts in the background afterward runs through the launcher. To close this path entirely, prevent handler registration with thedisableDeepLinkRegistrationsetting.
Helper process names in process monitors
With a launcher configured,ps and Activity Monitor show the versioned binary name for the background helper processes instead of Claude Code’s claude bg-pty-host and claude bg-spare labels, because the launcher’s exec rebuilds the argument list. The renaming is a side effect, not concealment: the processes are otherwise unchanged, and Claude Code identifies its own processes by binary path, never by display name.
Set up the launcher
1
Write the launcher script
Create an executable script at an absolute path, such as Make it executable with
/opt/corp/launcher. Claude Code runs it with the full Claude Code command as its arguments, and the script must end by calling exec "$@" so it replaces itself with Claude Code:chmod +x. The setup portion is whatever your launcher must do before Claude Code runs; the launcher contract below lists the rules the script has to follow.If you previously replaced the
~/.local/bin/claude symlink with your launcher, restore the original symlink in the same change. A replaced symlink makes the first wrapped session start the background service through both launchers at once, and it puts the installation into an externally managed state: /doctor reports it, auto-update leaves the file in place, and cleanup of old versions stays disabled until the installer manages that path again.2
Set CLAUDE_CODE_PROCESS_WRAPPER in settings
Set the variable in the When more than one source sets the variable, the managed settings value overrides both
env block of a settings file so the detached background service inherits it. A shell export isn’t enough: the background service starts on demand, outlives your shell, and never re-reads shell profiles.For one machine, add it to ~/.claude/settings.json. To deploy it to every machine in your organization, put the same block in managed settings:~/.claude/settings.json and a value exported in the shell, so users can’t point self-spawns at a different launcher.Project and local settings can’t set this variable. A file committed to a repository must not be able to put a binary in front of every Claude Code process on the machine, so CLAUDE_CODE_PROCESS_WRAPPER in .claude/settings.json or .claude/settings.local.json is ignored, with a warning in the debug log.3
Restart the background service and your sessions
A running background service and any open
claude sessions read the variable once at startup, so they keep launching unwrapped processes until restarted. Run claude daemon stop --any to stop the on-demand service; the next command that needs it, such as claude agents, starts a wrapped one. An installed service takes claude daemon stop without --any. Then restart your open claude sessions.On machines you can’t restart by hand, the first session started after the settings push retires a leftover unwrapped on-demand service automatically. A machine where no new session starts keeps its unwrapped service until one does, and an installed service always needs the restart in this step.4
Verify
Run
/status in a session: the Self-exec entry shows the resolved launch command and warns when the running background service doesn’t match it. claude daemon status prints the same information from the shell, including after you unset the variable, when /status no longer shows the entry.The launcher contract
When the launcher can’t run, Claude Code refuses to start the process instead of starting it unwrapped. On Windows, the variable is ignored and processes start unwrapped. Claude Code holds the script to these rules:- End with
exec "$@". A launcher that forks a child and exits leaves an orphaned Claude Code process the background service can’t track. Agent view marks such a session failed with a message naming the launcher, and the service reaps what the launcher left behind. - Don’t reorder, absorb, or prepend arguments. The first argument is the Claude Code binary and everything after it is its argv.
- Pass every inherited environment variable through to
exec. Adding variables, such as injected credentials, is fine; dropping inherited ones is not.- The per-session authentication tokens, the model and provider selection, and
CLAUDE_CODE_PROCESS_WRAPPERitself all travel on the inherited environment, so a launcher that rebuilds it from an allow list breaks the sessions it starts, and/statusreports a launcher mismatch. - If the launcher must enter a namespace or sandbox that resets the environment, re-export the inherited environment inside it verbatim.
- The per-session authentication tokens, the model and provider selection, and
- Reach
execwithin about three seconds each time the launcher runs. A cold background dispatch runs the launcher twice in series before the first byte of output, so do slow work such as a single sign-on exchange lazily or from a cache.- A launcher that runs far past the budget is treated as a stalled start and restarted.
- Tolerate being invoked from inside itself. Claude Code applies the launcher to every nested self-spawn, so a launcher that acquires an exclusive resource must detect that it already holds it.
- Don’t write to the terminal before Claude Code starts. Anything printed before the
execis reported as the crash cause if the session dies before initializing.
Format of the CLAUDE_CODE_PROCESS_WRAPPER value
For most launchers, the value is just the script’s absolute path, like /opt/corp/launcher.
To pass your launcher arguments of its own, write them after the path. Claude Code parses the value as an argument list, not a shell command:
- Whitespace separates tokens, and double quotes group a token that contains spaces.
- A value that starts with
[is read as a JSON string array, such as["/opt/corp/launcher", "--profile", "cc"]. - Shell syntax doesn’t work: there is no variable expansion or globbing, and an unquoted operator such as
;,|,&, or$(is rejected as a configuration error rather than reinterpreted.
Relationship to CLAUDE_CODE_SHELL_PREFIX
CLAUDE_CODE_PROCESS_WRAPPER wraps Claude Code’s own processes and passes the command through as separate argv tokens for the launcher to exec. CLAUDE_CODE_SHELL_PREFIX wraps the shell commands Claude runs on your behalf, such as Bash tool calls, hooks, and the commands that start stdio MCP servers, and passes each one as a single shell-quoted string in $1 for the wrapper to re-evaluate. A launcher written for one doesn’t work as the other.
Related resources
- Agent view: the background sessions and supervisor process the launcher covers
- Environment variables: the
CLAUDE_CODE_PROCESS_WRAPPERreference entry - Managed settings: deliver the
envblock across a fleet - Launcher error reference: the refusal messages and how to recover