Follow Discord
Sweep 27 Oct 2026 · 19:04Z Build v2.1.312 531 read Stable v2.1.308 Latest v2.1.312 Next v2.1.312 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One capture · claude-code

One read of Claude Code CLIclaude-code-20261024T173702Z

20 pages moved out of 228 read.

Pages moved 20 significant first
Pages read 228 in this capture
Captured 17:37 UTC
Corpus hash 44cd1b6c115e corpus-hash

What this read moved

1-20 of 20

code-review-customize Changed · +65 / -3 lines

### Include a skill file #### Write a skill line that Code Review includes #### Check what a review gets from a skill #### Characters that Code Review changes in skill text

from line 119
119119 
120120When a review doesn't apply an included file, check each of these:
121121 
122* **Location**: move the file to the repository root. A path into a subdirectory such as `@docs/review-rules.md` isn't included.
122* **Location**: move the file to the repository root. Except for a [skill file](#include-a-skill-file), a path into a subdirectory such as `@docs/review-rules.md` isn't included.
123123* **Name**: match the file name exactly, including case, and use a name without spaces. A line whose name matches no file beside `REVIEW.md` stays in `REVIEW.md` as plain text, and the check run doesn't report it as a skipped include.
124* **One level**: list every file in the root `REVIEW.md`. An `@` line in an included file or in a subdirectory `REVIEW.md` stays as plain text, and a Markdown link to another file isn't an include.
124* **One level**: list every file in the root `REVIEW.md`. An `@` line in an included file or in a subdirectory `REVIEW.md` stays as plain text ([skill files differ](#include-a-skill-file)), and a Markdown link to another file isn't an include.
125125* **Limits**: include at most eight different files, and keep `REVIEW.md` and the files it includes under 150,000 bytes combined. A file that would reach the byte limit is skipped whole, not cut short. The check run reports how many include lines it skipped for either limit.
126* **Links**: keep Markdown links at 64 or fewer across `REVIEW.md` and the files it includes. Links to `http`, `https`, or `mailto` addresses and to `#` anchors don't count. Past that limit, the review skips your whole root `REVIEW.md`, not only the included files. The check run then [says the instructions weren't applied](https://code.claude.com/docs/en/code-review-troubleshooting.md#the-check-run-mentions-review-md).
126* **Links**: keep Markdown links at 64 or fewer across `REVIEW.md` and the files it includes. Links to `http`, `https`, or `mailto` addresses and to `#` anchors don't count, and neither do links in a [skill file](#include-a-skill-file). Past that limit, the review skips your whole root `REVIEW.md`, not only the included files. The check run then [says the instructions weren't applied](https://code.claude.com/docs/en/code-review-troubleshooting.md#the-check-run-mentions-review-md).
127127* **Merged copy**: merge the included file into the [branch `REVIEW.md` is read from](#which-branch-review-md-is-read-from). The review reads both from the same commit, never from the pull request's own changes.
128128 
129### Include a skill file
130 
131To have reviews follow guidance you keep in a [skill](https://code.claude.com/docs/en/skills.md), include the skill's `SKILL.md` in your root `REVIEW.md`. Code Review doesn't run skills.
132 
133Add `@` and the path to `SKILL.md`, in the form `.claude/skills/<folder>/SKILL.md`, on a line of its own:
134 
135```markdown theme={null}
136# Review instructions
137 
138Report at most five Nits per review.
139 
140@.claude/skills/api-review/SKILL.md
141 
142Treat any change under `migrations/` as Important.
143```
144 
145Protect the [branch that `REVIEW.md` is read from](#which-branch-review-md-is-read-from), because Code Review includes a skill file only when it reads `REVIEW.md` from a protected branch. The skill file counts toward the limits of eight files and 150,000 bytes, and has its own limit of 30,000 bytes.
146 
147#### Write a skill line that Code Review includes
148 
149Use a folder name without spaces. A line with a space in its path stays in `REVIEW.md` as plain text, and the check run doesn't report it as a skipped include.
150 
151If the `@` line for a skill breaks one of the rules below, Code Review doesn't include the skill from that line:
152 
153* **Path**: write the path as `.claude/skills/<folder>/SKILL.md` from the repository root, with the skill's folder directly under `skills/`. Unlike a file name at the root, a path of this form that matches no file is skipped and counted.
154* **Source**: copy guidance from any other file under `.claude/`, or from a [bundled, personal, or plugin skill](https://code.claude.com/docs/en/skills.md#where-skills-live), into a file beside `REVIEW.md`. You can't include those directly.
155* **File**: commit `SKILL.md` as a regular text file. Code Review doesn't follow a symbolic link, whether the link is the file or a folder in its path.
156* **Size**: keep `SKILL.md` at 30,000 bytes or less, frontmatter included
157* **Count**: write at most eight skill lines. A repeated line counts again toward the eight.
158* **Blocks in `REVIEW.md`**: keep the `@` line out of code fences. Close every HTML comment and every `<pre>`, `<script>`, `<style>`, or `<textarea>` block above it.
159 
160When Code Review skips a skill line for breaking the File, Size, Count, or Blocks rule, the check run's [message about skipped include lines](https://code.claude.com/docs/en/code-review-troubleshooting.md#the-check-run-mentions-review-md) counts it. The message appears only when the review applies `REVIEW.md` from a protected branch.
161 
162#### Check what a review gets from a skill
163 
164When the skill is included, a review gets the body of `SKILL.md` and nothing else from the skill, so check the skill against the list below.
165 
166* **Frontmatter**: repeat in the body anything a review needs from the [frontmatter](https://code.claude.com/docs/en/skills.md#frontmatter-reference), for example the conditions that `description` gives for using the skill
167* **Injected commands**: write out what a [`!` command](https://code.claude.com/docs/en/skills#inject-dynamic-context) would supply. Code Review doesn't run the command, and replaces the whole line or block that holds it with a placeholder.
168* **Substitutions**: replace `$ARGUMENTS` and other [substitutions](https://code.claude.com/docs/en/skills.md#available-string-substitutions) with fixed text, because a review passes no arguments and leaves them as written
169* **Supporting files**: copy what a review needs into `SKILL.md`, because Code Review doesn't load scripts, linked files, or `@file` references
170* **Exclusions and Nit caps**: write instructions that exclude findings or cap Nits in `REVIEW.md` or a file beside it, not in the skill. Code Review doesn't accept a skill's text as the reason for either.
171* **Links to repository files**: state in words what a link or `@file` reference points to. The review sees the path as plain text or as a placeholder.
172 
173If you edit the skill's `!` commands, `$ARGUMENTS`, or links, you also change how it works in Claude Code. If your team relies on those, copy the guidance into a file beside `REVIEW.md` and include that file in place of the skill.
174 
175#### Characters that Code Review changes in skill text
176 
177Code Review changes some characters in the skill's text, so your code samples reach a review in the form below.
178 
179```text theme={null}
180In SKILL.md: Use `items[0]`, never `List<String>`.
181The review reads: Use ˋitems(0)ˋ, never ˋList< String>ˋ.
182 
183In SKILL.md: Run `ps | grep api` and ask @alice.
184The review reads: Run ˋps ¦ grep apiˋ and ask @ alice.
185```
186 
187Where a rule depends on one of those characters, state it in words too.
188 
129189### Which branch REVIEW\.md is read from
130190 
131191Code Review reads `REVIEW.md` from the pull request's base branch or from the default branch, never from the pull request's own changes. To change review behavior, merge the `REVIEW.md` change first.
from line 195
135195* Lowering a finding's severity
136196* Excluding paths, files, or classes of issue
137197* Capping nit comments
198 
199Write exclusions and Nit caps in `REVIEW.md` or a file beside it, not in an [included skill](#include-a-skill-file).
138200 
139201#### What counts as branch protection
140202 

desktop-scheduled-tasks Changed · +22 / -3 lines

### Full-page form

from line 11
1111 
1212This page covers local scheduled tasks; for cloud scheduled tasks and their trigger options, see [Automate work with scheduled tasks](https://code.claude.com/docs/en/routines.md).
1313 
14> [!NOTE]
15>
16> If your app shows the feature under its [earlier name](https://code.claude.com/docs/en/routines.md#where-the-name-routines-still-appears), click **Routines** wherever this page says **Scheduled** and **New routine** where it says **New task**.
17 
1418## Compare scheduling options
1519 
1620Claude Code offers three ways to schedule recurring or one-off work:
from line 45
4145 
4246**1. Open the Scheduled tasks page**
4347 
44In the [**Code** tab](https://code.claude.com/docs/en/desktop.md), click **Scheduled** in the sidebar or in the sidebar's **More** menu.
48In the [**Code** tab](https://code.claude.com/docs/en/desktop.md), click **Scheduled** in the sidebar or in the sidebar's **More** menu. If the sidebar shows **Routines** instead of **Scheduled**, click **Routines**, then click **New routine**, and continue with [Full-page form](#full-page-form).
4549 
4650**2. Start a new task**
4751 
48Click **New task**. If a menu opens with **Create with Claude** and **Set up manually**, select **Set up manually**. The **New scheduled task** dialog opens. If **Only on this computer** is at the top of the dialog, go to [Dialog with Only on this computer at the top](#dialog-with-only-on-this-computer-at-the-top) before you enter anything.
52Click **New task** to open the **New scheduled task** dialog. Check for these screens before you continue:
4953 
54* If a menu opens with **Create with Claude** and **Set up manually**, select **Set up manually** to open the dialog.
55* If **Only on this computer** is at the top of the dialog, go to [Dialog with Only on this computer at the top](#dialog-with-only-on-this-computer-at-the-top) before you enter anything.
56* If a menu with **Local** and **Cloud** opens, or a full page opens in place of the dialog, go to [Full-page form](#full-page-form).
57 
5058**3. Name the task**
5159 
5260Enter a name in the **Name** field. Desktop converts it to lowercase kebab-case and uses it as the task's folder name on disk, so the name must be unique across your tasks.
from line 103
95103 
96104Click **Create**. The task's detail page opens.
97105 
106### Full-page form
107 
108If a menu with **Local** and **Cloud** opens, select **Local**. A page titled **New local routine** or **New local scheduled task** opens. Fill in its fields:
109 
110* **Name**: enter a name that is unique across your tasks.
111* **Description**: enter one.
112* **Instructions**: enter what Claude should do when the task runs. In the same box, select the permission mode, the model, and the folder the task runs in.
113* **Schedule**: select one of the [schedule options](#schedule-options).
114 
115If **Create** is unavailable, hover over it to see what the form still needs. Click **Create**, and the task's detail page opens.
116 
98117## Schedule options
99118 
100When you turn on **Only on this computer** under **Advanced settings**, the **Trigger** card offers Hourly, Daily, Weekdays, and Weekly. To get **Manual** or **Custom** for such a task, open its [**Edit** form](#manage-scheduled-tasks) after you create it. That form and the [dialog with **Only on this computer** at the top](#dialog-with-only-on-this-computer-at-the-top) offer these six options:
119When you turn on **Only on this computer** under **Advanced settings**, the **Trigger** card offers Hourly, Daily, Weekdays, and Weekly. To get **Manual** or **Custom** for such a task, open its [**Edit** form](#manage-scheduled-tasks) after you create it. That form, the [dialog with **Only on this computer** at the top](#dialog-with-only-on-this-computer-at-the-top), and the [full-page form](#full-page-form) offer these six options:
101120 
102121* **Hourly**: runs every hour
103122* **Daily**: runs every day at the local time you pick

fast-mode Changed · +29 / -19 lines

#### Fast mode unavailable due to network connectivity issues #### Fast mode is currently unavailable #### Fast mode has been disabled by your organization

from line 151
151151 
152152Before offering fast mode, Claude Code checks your organization's fast mode availability with a request directly to `api.anthropic.com`. The check doesn't follow [`ANTHROPIC_BASE_URL`](https://code.claude.com/docs/en/llm-gateway-connect#set-the-base-url-and-credential), so on a network that routes Claude traffic through an [LLM gateway](https://code.claude.com/docs/en/llm-gateway) and blocks direct egress to `api.anthropic.com`, the check fails even though inference requests work. The check does use a configured [HTTP proxy](https://code.claude.com/docs/en/network-config.md#proxy-configuration), so a network block fails the check only where `api.anthropic.com` is unreachable even through the proxy.
153153 
154When the check fails, `/fast` reports "Fast mode unavailable due to network connectivity issues", and requests run at standard speed, even when your organization has fast mode enabled. A check that succeeded in the past keeps working from its cached result, so a blocked check mostly affects new installations.
154If your organization has fast mode enabled and `/fast` says otherwise, find the message it shows among these cases.
155155 
156The same connectivity message appears on an open network when the check reaches `api.anthropic.com` but presents a credential Anthropic rejects. A session whose resolved key is a gateway-issued credential, held in [`ANTHROPIC_API_KEY`](https://code.claude.com/docs/en/llm-gateway-connect#set-the-base-url-and-credential) or produced by an [`apiKeyHelper`](https://code.claude.com/docs/en/settings-reference#apikeyhelper), sends the check with that key, and the rejected request is reported as a connectivity failure.
156#### Fast mode unavailable due to network connectivity issues
157157 
158To restore fast mode, allowlist direct egress to `api.anthropic.com` where a network block is the cause, or set whichever variable matches how the check fails:
158The availability check failed, for example because your network blocks `api.anthropic.com`, and requests run at standard speed. A check that succeeded in the past keeps working from its cached result, so a blocked check mostly affects new installations.
159159 
160* `CLAUDE_CODE_SKIP_FAST_MODE_NETWORK_ERRORS=1` treats a failed check as available and still honors a "disabled by your organization" response. Use it when your network refuses the connection, or when Anthropic rejects a gateway credential; allowlisting doesn't help the credential case, since nothing is blocked.
161* `CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK=1` skips the check entirely. Use it when your network intercepts the request rather than refusing it.
160To restore fast mode, allowlist direct egress to `api.anthropic.com`, or set `CLAUDE_CODE_SKIP_FAST_MODE_NETWORK_ERRORS=1`, which treats a failed check as available and still honors a "disabled by your organization" response.
162161 
163Two gateway configurations report "Fast mode has been disabled by your organization" rather than the connectivity message, even when your organization has fast mode enabled:
162#### Fast mode is currently unavailable
164163 
165* A session that authenticates with [`ANTHROPIC_AUTH_TOKEN`](https://code.claude.com/docs/en/llm-gateway-connect#set-the-base-url-and-credential) alone skips the check: without a claude.ai login or an Anthropic API key, and without a cached successful check, Claude Code treats fast mode as disabled by your organization without sending the request.
166* A proxy that intercepts the check and answers with its own page, for example a TLS-inspecting proxy returning an HTTP 200 block page, is read as a response saying your organization has fast mode disabled.
164Behind a gateway, check for these configurations, in which Claude Code doesn't send the availability check and has no earlier successful check cached:
167165 
168In both cases, set `CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK=1` to restore fast mode. `CLAUDE_CODE_SKIP_FAST_MODE_NETWORK_ERRORS` doesn't apply to either case, since it only bypasses failed checks and both of these produce a disabled response instead. Allowlisting direct egress doesn't help the bearer-token case, which never sends the request.
166* You point `ANTHROPIC_BASE_URL` at your gateway and set [`ANTHROPIC_API_KEY`](https://code.claude.com/docs/en/llm-gateway-connect#set-the-base-url-and-credential) to a gateway-issued key that isn't in the format of an Anthropic API key. Claude Code withholds such a key from `api.anthropic.com`.
167* You set [`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`](https://code.claude.com/docs/en/env-vars), so Claude Code skips the check.
169168 
170The variables affect only the client-side check. When your organization has fast mode disabled, the API rejects fast mode requests whether or not they're set. A rejection from the API stands even with a skip variable set. Claude Code retries the rejected request at standard speed, turns fast mode off, and `/fast` reports that your organization has disabled fast mode.
169To restore fast mode in either configuration, set `CLAUDE_CODE_SKIP_FAST_MODE_NETWORK_ERRORS=1`.
171170 
172Setting `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` also suppresses the availability check. Without a previously cached successful check, `/fast` reports "Fast mode is currently unavailable"; both skip variables restore fast mode in that configuration too.
171#### Fast mode has been disabled by your organization
172 
173Behind a gateway, you can see this message even when your organization has fast mode enabled, in either of these configurations:
174 
175* You authenticate with [`ANTHROPIC_AUTH_TOKEN`](https://code.claude.com/docs/en/llm-gateway-connect#set-the-base-url-and-credential) alone, with no claude.ai login, no API key, and no cached successful check. Claude Code then treats fast mode as disabled by your organization without sending the request.
176* A proxy intercepts the check and answers with its own page, for example a TLS-inspecting proxy returning an HTTP 200 block page. Claude Code reads that page as a response saying your organization has fast mode disabled.
177 
178In both cases, set `CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK=1`, which skips the check entirely, rather than `CLAUDE_CODE_SKIP_FAST_MODE_NETWORK_ERRORS`, which doesn't apply to either.
179 
180> [!NOTE]
181>
182> The skip variables change only the client-side check. When your organization has fast mode disabled, the API rejects fast mode requests whether or not they're set. Claude Code then retries the request at standard speed, turns fast mode off, and `/fast` reports that your organization has disabled fast mode.
173183 
174184### Require per-session opt-in
175185 

hooks Changed · +13 / -0 lines

#### PowerShell started no program ### Reference scripts by path in PowerShell hooks

This page is larger than the 256 KiB this site keeps, so one side of the diff below stops where the stored text does.

from line 957
957957blocking because Git Bash wasn't found, so only exit code 0 or 2 counts as this hook's verdict.
958958```
959959 
960If the error also says `PowerShell started no program`, start with [the fixes for a `command` that is only quoted strings](#powershell-started-no-program), which PowerShell prints instead of running.
961 
960962Without Git Bash, any exit code other than 0 or 2 from that hook counts as a [failure](#block-the-action-when-a-hook-fails), even when it prints JSON that doesn't deny the call. To find out why the hook failed, check the [debug log](#debug-hooks). Then pick the fix that matches your hook:
961963 
962964* **The call should go ahead when the hook fails**: add [`"onFailure": "continue"`](#block-the-action-when-a-hook-fails) to the hook
from line 969
967969 
968970Before v2.1.309, the message read `blocking because Git Bash wasn't found and this hook doesn't set "shell" to "powershell", so only exit code 0 or 2 counts as its verdict.`
969971 
972#### PowerShell started no program
973 
974A hook with this error ran in PowerShell, which prints a `command` that is only quoted strings, such as `"command": "\"C:\\Tools\\guard.exe\""`, instead of running it.
975 
976* **To print text, such as JSON**: set `"shell": "powershell"`, and the hook exits 0
977* **To run a `.ps1`, `.cmd`, or `.bat` file, or a `.exe` whose path has a variable**: set `"shell": "powershell"` and put `&` before the quoted path. Leave `$HOME` as written, and spell environment variables as the [`check.ps1` example](#reference-scripts-by-path-in-powershell-hooks) spells `$env:CLAUDE_PROJECT_DIR`
978* **To run one `.exe` at a path without `$`**: remove the inner quotes from `command`, then apply the [fix for a hook that runs one `.exe`](#failed-hooks-on-windows-without-git-bash), writing `"args": []` if the program takes no arguments
979* **To run a script written for Bash**: apply the [fix for a hook written for Bash](#failed-hooks-on-windows-without-git-bash)
980 
970981#### Exit code 2 behavior per event
971982 
972983Exit code 2 is the way a hook signals "stop, don't do this." The effect depends on the event, because some events represent actions that can be blocked (like a tool call that hasn't happened yet) and others represent things that already happened or can't be prevented.
from line 3897
38863897 
38873898Instead of executing a Bash command, prompt-based hooks:
38883899 
38891. Send the hook input and your prompt to a Claude model, by default the one Claude Code uses for [background functionality](https://code.claude.com/docs/en/costs.md#background-token-usage)
38902. The LLM responds with structured JSON containing a decision
38913. Claude Code processes the decision automatically
3892 
3893### Prompt hook configuration
3894 
3895Set `type` to `"prompt"` and provide a `prompt` string instead of a `command`. Use the `$ARGUMENTS` placeholder to inject the hook's JSON input data into your prompt text.
3896 
3897In a prompt or [agent hook](#agent-based-hooks), you can write the `prompt` as a rule about what to block or allow, such as "Block any Bash command that reads `.env` files", or as a condition that must hold, such as "All unit tests pass".
3898 
3899This `Stop` hook asks the LLM to evaluate whether all tasks are complete before allowing Claude to finish:
3900 
3901```json theme={null}
3902{
3903 "hooks": {
3904 "Stop": [
3905 {
3906 "hooks": [
3907 {
3908 "type": "prompt",
3909 "prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks are complete."
3910 }
3911 ]
3912 }
3913 ]
3914 }
3915}
3916```
3917 
3918| Field | Required | Description |
3919| :- | :- | :- |
3920| `type` | yes | Must be `"prompt"` |
3921| `prompt` | yes | The prompt text to send to the LLM. Use `$ARGUMENTS` as a placeholder for the hook input JSON. If `$ARGUMENTS`
39001. Send the hook input and your prompt to a Claude model, by default the one Claude Code uses for [background functionality](https://code.claude.com/docs/en/costs.md#ba

llm-gateway-connect Changed · +3 / -2 lines

from line 515
515515| `400` errors on every request, in the gateway's own words rejecting a tool's input schema or its `pattern`, on Claude Code v2.1.265 through v2.1.267 | In a gradual rollout on those versions, the [Artifact tool](https://code.claude.com/docs/en/artifacts.md#availability) schema carries a regular expression with `\p{...}` Unicode character classes. The Anthropic API accepts it, but a gateway or upstream that checks each tool schema's `pattern` with its own regex engine rejects the whole request | Update to v2.1.268 or later, which doesn't send the regular expression. On an affected version, [turn artifacts off](https://code.claude.com/docs/en/artifacts.md#disable-artifacts), which removes the tool and its schema from requests |
516516| `400` errors on every request, in the gateway's own words rejecting an unrecognized tool type, such as `Input tag 'advisor_20260301'`, on Claude Code v2.1.275 | In a gradual rollout on that version, requests carry an [advisor tool](https://code.claude.com/docs/en/advisor.md) entry even with the advisor off. The Anthropic API accepts it, but a gateway or upstream that validates tool types rejects the whole request; one that [forwards request body fields unchanged](https://code.claude.com/docs/en/llm-gateway-protocol.md#forward-as-open-lists) passes it through unaffected. The entry is a declaration that carries no conversation content | Update to v2.1.276 or later, which doesn't send the entry behind an `ANTHROPIC_BASE_URL` gateway unless you turn the advisor on. On v2.1.275, set [`CLAUDE_CODE_DISABLE_ADVISOR_TOOL=1`](https://code.claude.com/docs/en/env-vars), which removes the entry from requests |
517517| Models missing from the `/model` picker | Gateway model names aren't in Claude Code's built-in list, or Claude Code is showing a [`modelPicker`](https://code.claude.com/docs/en/settings-reference#modelpicker) lineup that replaces the built-in options | Enable [gateway model discovery](#add-gateway-models-to-the-model-picker) or add names with the [model configuration](https://code.claude.com/docs/en/model-config.md) variables. If Claude Code shows a replacing `modelPicker` lineup, add the gateway models to it, or ask your administrator to add them when managed settings supply it |
518| `/fast` reports `Fast mode unavailable due to network connectivity issues` while inference requests work | The [fast mode](https://code.claude.com/docs/en/fast-mode.md) availability check goes directly to `api.anthropic.com` and doesn't follow `ANTHROPIC_BASE_URL`, so blocked direct egress fails the check. The same message appears on an open network when the check presents a gateway-issued key from `ANTHROPIC_API_KEY` or an `apiKeyHelper` and Anthropic rejects it | Allowlist `api.anthropic.com` if egress is blocked, or set a skip variable; for a rejected gateway key only the skip variables help. See [use fast mode behind proxies and LLM gateways](https://code.claude.com/docs/en/fast-mode.md#use-fast-mode-behind-proxies-and-llm-gateways) |
519| `/fast` reports `Fast mode has been disabled by your organization` in a session authenticated with `ANTHROPIC_AUTH_TOKEN`, even though the organization has fast mode enabled | The availability check requires a claude.ai login or an Anthropic API key; with only a bearer token, Claude Code treats fast mode as disabled without sending the check | Set `CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK=1`; see [use fast mode behind proxies and LLM gateways](https://code.claude.com/docs/en/fast-mode.md#use-fast-mode-behind-proxies-and-llm-gateways) |
518| `/fast` reports `Fast mode unavailable due to network connectivity issues` while inference requests work | The [fast mode](https://code.claude.com/docs/en/fast-mode.md) availability check goes directly to `api.anthropic.com` and doesn't follow `ANTHROPIC_BASE_URL`, so blocked direct egress fails the check | Allowlist `api.anthropic.com` if egress is blocked, or set a skip variable. See [use fast mode behind proxies and LLM gateways](https://code.claude.com/docs/en/fast-mode.md#use-fast-mode-behind-proxies-and-llm-gateways) |
519| `/fast` reports `Fast mode is currently unavailable` in a session that authenticates with a gateway-issued key in `ANTHROPIC_API_KEY` | While `ANTHROPIC_BASE_URL` points at your gateway, Claude Code withholds from `api.anthropic.com` a key that isn't in the format of an Anthropic API key. The [fast mode](https://code.claude.com/docs/en/fast-mode.md) availability check then isn't sent, and without an earlier successful check cached, Claude Code treats fast mode as unavailable | Set `CLAUDE_CODE_SKIP_FAST_MODE_NETWORK_ERRORS=1`; see [use fast mode behind proxies and LLM gateways](https://code.claude.com/docs/en/fast-mode.md#use-fast-mode-behind-proxies-and-llm-gateways) |
520| `/fast` reports `Fast mode has been disabled by your organization` in a session authenticated with `ANTHROPIC_AUTH_TOKEN`, even though the organization has fast mode enabled | The availability check requires a claude.ai login or an API key; with only a bearer token, Claude Code treats fast mode as disabled without sending the check | Set `CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK=1`; see [use fast mode behind proxies and LLM gateways](https://code.claude.com/docs/en/fast-mode.md#use-fast-mode-behind-proxies-and-llm-gateways) |
520521| Claude Code asks you to log in even though the [curl test](#verify-the-connection) succeeds | The CLI has no credential of its own: a reachable base URL isn't one, and in an interactive session an `env` block in a project's `.claude/settings.json` or `.claude/settings.local.json` applies only after the first-run wizard and [trust prompt](https://code.claude.com/docs/en/permissions.md#what-runs-before-you-trust-a-folder) | Set `ANTHROPIC_AUTH_TOKEN` somewhere Claude Code reads before first-run setup: a shell export, the `env` block in `~/.claude/settings.json`, or managed settings |
521522| `ANTHROPIC_API_KEY` is set but ignored, with no prompt | The key needs a one-time approval in interactive sessions, and a previously declined key is ignored without asking again | Enable it under `/config` with the `Use custom API key` option |
522523| `This machine's managed settings require a first-party login`, or [`Administrator policy requires a Cloud gateway sign-in`](https://code.claude.com/docs/en/errors#administrator-policy-requires-a-cloud-gateway-sign-in) when managed settings set `forceLoginMethod` to `"gateway"` or also set `forceLoginGatewayUrl` | Managed settings include `forceLoginMethod` or `forceLoginOrgUUID`, which cannot coexist with `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, or `apiKeyHelper` | Your administrator must remove `forceLoginMethod`, `forceLoginOrgUUID`, and `forceLoginGatewayUrl` from managed settings to use gateway credentials, or remove the gateway credential and use the sign-in the managed settings require. The two cannot be combined |

model-config Changed · +7 / -2 lines

from line 345
345345 
346346When the managed settings Claude Code applies define `availableModels`, that list alone applies, apart from a [host platform that supplies its own](https://code.claude.com/docs/en/settings.md#exceptions-to-managed-settings-precedence): entries in user, project, or local settings cannot extend it, and Claude Code never merges `availableModels` across managed sources either; [how Claude Code combines managed sources](https://code.claude.com/docs/en/managed-settings.md#how-claude-code-combines-managed-sources) says which source's list applies. Otherwise, lists from user, project, and local settings are [concatenated and deduplicated](https://code.claude.com/docs/en/settings.md#settings-precedence) like other array settings. Before Claude Code v2.1.175, entries from lower-precedence scopes merged into the managed list instead of being replaced by it.
347347 
348Within the effective list, an entry naming a specific model in a family, whether a version prefix or a full model ID, disables that family's wildcard entry: `["sonnet", "claude-sonnet-4-5"]` allows only Sonnet 4.5 versions, not every Sonnet model.
348Within the effective list, an entry naming a specific model in a family, whether a version prefix or a full model ID, disables that family's wildcard entry. For example, with `["sonnet", "claude-sonnet-4-5"]`, users can pick only the model IDs that `claude-sonnet-4-5` [matches](#restrict-model-selection), not every Sonnet model.
349349 
350350### Mantle model IDs
351351 
from line 943
943943>
944944> An `availableModels` allowlist delivered through [MDM or a managed settings file](https://code.claude.com/docs/en/managed-settings.md#delivery-mechanisms) still applies when using third-party providers; [server-managed settings are not delivered there](https://code.claude.com/docs/en/server-managed-settings.md#platform-availability).
945945>
946> Filtering matches on a model alias such as `opus`, a version prefix such as `claude-opus-4-8`, or the full provider-form model ID. Provider-specific prefixes such as `us.anthropic.` are not stripped, so to allow a specific model, list its full provider-form ID, or map it through [`modelOverrides`](#override-model-ids-per-version). For a pinned model, that ID is the value you set in its `ANTHROPIC_DEFAULT_*_MODEL` variable. Any `[1m]` suffix is stripped from both the allowlist entry and the requested model before matching.
946> Filtering matches on a model alias such as `opus`, a version prefix such as `claude-opus-4-8`, or the full provider-form model ID. Provider-specific prefixes such as `us.anthropic.` are not stripped, so to allow a specific model, map it through [`modelOverrides`](#override-model-ids-per-version) or list its full provider-form ID:
947>
948> * **Google Cloud's Agent Platform**: when the ID Claude Code requests carries an `@` date, such as `claude-sonnet-4-5@20250929`, include the date in your allowlist entry. On its own, `claude-sonnet-4-5` doesn't match that ID under either [`availableModelsMatch`](https://code.claude.com/docs/en/settings-reference#availablemodelsmatch) setting.
949> * **Pinned models**: list the value you set in the model's `ANTHROPIC_DEFAULT_*_MODEL` variable
950>
951> Any `[1m]` suffix is stripped from both the allowlist entry and the requested model before matching.
947952 
948953### Customize pinned model display and capabilities
949954 

network-config Changed · +3 / -1 lines

### Cloud provider and LLM gateway routing

from line 236
236236 
237237The two Datadog intake hosts carry only optional operational telemetry, and setting [`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`](https://code.claude.com/docs/en/env-vars) disables both. Sessions on third-party providers never send to these hosts, even when a platform sets [`CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST`](https://code.claude.com/docs/en/env-vars) and telemetry metrics default on. See [Telemetry services](https://code.claude.com/docs/en/data-usage.md#telemetry-services) for everything Claude Code sends and how to disable it before finalizing your allowlist.
238238 
239### Cloud provider and LLM gateway routing
240 
239241When using [Amazon Bedrock](https://code.claude.com/docs/en/amazon-bedrock.md), [Google Cloud's Agent Platform](https://code.claude.com/docs/en/google-vertex-ai.md), [Microsoft Foundry](https://code.claude.com/docs/en/microsoft-foundry.md), or a signed-in [Claude apps gateway](https://code.claude.com/docs/en/claude-apps-gateway) session, model traffic and authentication go to your provider or gateway instead of `api.anthropic.com`, `claude.ai`, or `platform.claude.com`. The WebFetch tool still calls `api.anthropic.com` for its [domain safety check](https://code.claude.com/docs/en/data-usage.md#webfetch-domain-safety-check) unless you set `skipWebFetchPreflight: true` in [settings](https://code.claude.com/docs/en/settings.md).
240242 
241When routing through an [LLM gateway](https://code.claude.com/docs/en/llm-gateway) with [`ANTHROPIC_BASE_URL`](https://code.claude.com/docs/en/llm-gateway-connect#set-the-base-url-and-credential), the [fast mode](https://code.claude.com/docs/en/fast-mode.md) availability check still calls `api.anthropic.com` rather than the gateway base URL. The check does honor a configured HTTP proxy, so where a network block is the cause, an allowlist entry for `api.anthropic.com` in the proxy is the fix. A network block fails the check only where the host is unreachable even through the proxy, and fast mode then reports a connectivity error. The same connectivity error appears when the check presents a gateway-issued credential that Anthropic rejects; allowlisting doesn't help there, since nothing is blocked. See [use fast mode behind proxies and LLM gateways](https://code.claude.com/docs/en/fast-mode.md#use-fast-mode-behind-proxies-and-llm-gateways) for the variables that restore it.
243If your developers use [fast mode](https://code.claude.com/docs/en/fast-mode.md) behind an [LLM gateway](https://code.claude.com/docs/en/llm-gateway) set with [`ANTHROPIC_BASE_URL`](https://code.claude.com/docs/en/llm-gateway-connect#set-the-base-url-and-credential), keep `api.anthropic.com` reachable through your proxy. When Claude Code sends the fast mode availability check, it goes to that host through any configured HTTP proxy, never to the gateway. For when the check isn't sent, and the variable that restores fast mode in each case, see [Use fast mode behind proxies and LLM gateways](https://code.claude.com/docs/en/fast-mode.md#use-fast-mode-behind-proxies-and-llm-gateways).
242244 
243245### Organization IP allowlists and proxy egress
244246 

routines Changed · +20 / -2 lines

#### Full-page form

from line 21
2121* A task with only an API trigger runs when you call its endpoint.
2222* A task with only a GitHub trigger runs when a matching repository event occurs.
2323 
24Scheduled tasks are available on Pro, Max, Team, and Enterprise plans. Create and manage them at [claude.ai/code/routines](https://claude.ai/code/routines), or from the CLI with `/schedule`. The earlier name, routines, [still appears](#where-the-name-routines-still-appears) in the web address, the CLI, and the API.
24Scheduled tasks are available on Pro, Max, Team, and Enterprise plans. Create and manage them at [claude.ai/code/routines](https://claude.ai/code/routines), or from the CLI with `/schedule`. The earlier name, routines, [still appears](#where-the-name-routines-still-appears).
2525 
2626> [!NOTE]
2727>
from line 62
6262 
6363**1. Open the creation dialog**
6464 
65Go to [claude.ai/code/routines](https://claude.ai/code/routines) and click **New task**. If a menu opens first, select **Set up manually**. The **New scheduled task** dialog opens. The menu's other option, **Create with Claude**, lets you describe the task in a chat and have Claude create it instead.
65Go to [claude.ai/code/routines](https://claude.ai/code/routines) and click **New task** to open the **New scheduled task** dialog. Check for these screens before you continue:
6666 
67* If the button reads **New routine** instead of **New task**, click **New routine** and continue with [Full-page form](#full-page-form).
68* In the Desktop app, if a menu with **Local** and **Cloud** opens, go to [Full-page form](#full-page-form).
69* If a menu opens with **Create with Claude** and **Set up manually**, select **Set up manually** to open the dialog. **Create with Claude** lets you describe the task in a chat and have Claude create it instead.
70* If a full page opens in place of the dialog, go to [Full-page form](#full-page-form).
71 
6772If **Repositories** and **Environment** are already showing when the dialog opens, see [how that dialog differs](#dialog-with-repositories-and-environment-in-the-main-view). Otherwise, this dialog also creates scheduled tasks for claude.ai chat. To make yours what the dialog calls a Claude Code task, select a repository or add a GitHub event or API trigger in the steps that follow. That kind of task runs Claude Code in a cloud session.
6873 
6974**2. Name the task and write the prompt**
from line 127
122127 
123128Select the repository before you add a GitHub event trigger, because that trigger is unavailable until a repository is selected.
124129 
130#### Full-page form
131 
132In the Desktop app, if a menu with **Local** and **Cloud** opens, select **Cloud**. A page titled **New routine** or **New scheduled task** opens. Fill in its fields:
133 
134* **Name**: enter one.
135* **Instructions**: enter the prompt. In the same box, select the model, the repositories, and the environment. For what each one controls, see the steps under [Create from the web](#create-from-the-web).
136* **Select a trigger**: select **Schedule**, **GitHub event**, or **API**. For a schedule, see [Add a schedule trigger](#add-a-schedule-trigger). For a [GitHub event](#add-a-github-trigger), select a repository. For an [API trigger](#add-an-api-trigger), copy the token from the **API token** dialog that opens after you click **Create**, because it is shown once. To combine several triggers, click **Add another trigger**.
137* **Connectors**: all of your connected connectors are included by default, so remove the ones the task doesn't need.
138 
139If **Create** is unavailable, hover over it to see what the form still needs. Click **Create**, and the task's detail page opens.
140 
125141### Create from the CLI
126142 
127143Run `/schedule` in any session to create a scheduled task conversationally. You can also pass a description directly, for a recurring task like `/schedule daily PR review at 9am` or a one-off like `/schedule clean up feature flag in one week`. Claude walks through the same information the web dialog collects, then saves the task to your account. The command is also available under the alias `/routines`.
from line 164
148164* **Runs on the hour**: if you schedule a run exactly on the hour, such as 9:00, it can start several minutes late. To start close to the scheduled time, pick a few minutes past the hour, for example 9:07.
149165* **Custom intervals**: for a cadence the presets lack, create the task and select **Custom** in its [edit dialog](#edit-and-control-scheduled-tasks). Then enter a cron expression, such as `0 */2 * * *` for every two hours. To set one from the CLI, run [`/schedule update`](#manage-scheduled-tasks-from-the-cli). Expressions that run more often than once per hour are rejected.
150166* **Time zone**: when you pick a preset frequency, enter the time in your local time zone.
167* **Cron expression time zone**: when the expression is five fields alone, write the hours in UTC, so `0 9 * * 1-5` runs at 9:00 AM UTC on weekdays. If the field opens with a `CRON_TZ=` prefix, keep it and write the hours in its zone, or delete it and write them in UTC.
151168 
152169#### Schedule a one-off run
153170 
from line 468
451468 
452469Scheduled tasks were previously called routines, and these places keep the earlier name. Each one refers to the same feature:
453470 
471* **App**: in some accounts the sidebar entry and page title read **Routines**, the button reads **New routine**, and other labels say routine. To create a task there, see [Full-page form](#full-page-form).
454472* **Web address**: `claude.ai/code/routines`
455473* **CLI**: the `/routines` alias for `/schedule`, the command's description, and Claude's replies while it sets up a task
456474* **API**: the `/fire` endpoint path, its beta header, its response `type`, the `<routine-fire-payload>` tag, and the [Trigger a routine through the API](https://platform.claude.com/docs/en/api/claude-code/routines-fire) reference page

sandboxing Changed · +13 / -1 lines

#### When the sandbox deletes `HEAD`, `objects`, or `refs` ### A folder named `objects` or `refs` disappears after a command

from line 533
533533 
534534* **In your working directory and the directories above it**: the `.claude` settings files, the `.claude/skills`, `.claude/agents`, `.claude/commands`, and `.claude/hooks` directories, `.mcp.json`, and the files Claude Code runs on its own, such as `.claude/workflows` and `.claude/scheduled_tasks.json`
535535* **In your working directory only**: shell startup files such as `.bashrc` and `.zshrc`, `.gitconfig`, the `.vscode` and `.idea` directories, and `hooks` and `config` inside `.git`
536* **Files that would turn your working directory into a bare git repository**: `HEAD`, `objects`, and `refs` at the top level, plus existing `config` and `hooks` entries there when a `HEAD` sits beside them. A file named `config` is denied even with no `HEAD`. On Linux and WSL2, the sandbox deletes a top-level `HEAD` file or `objects` or `refs` directory that appears while a sandboxed command is running
536* **Files that would turn your working directory into a bare git repository**: `HEAD`, `objects`, and `refs` at the top level, plus existing `config` and `hooks` entries there when a `HEAD` file or symlink sits beside them. A file named `config` is denied even with no `HEAD`. [A `HEAD`, `objects`, or `refs` that appears later can be deleted](#when-the-sandbox-deletes-head-objects-or-refs). On Linux and WSL2, a sandboxed command can create one where none exists.
537537* **In `~/.claude`, or the directory `CLAUDE_CONFIG_DIR` points to**: most of its contents, plus `~/.claude.json` and the `.credentials.json` credential store
538538 
539539If a symlink appears at a protected settings file's path during the session, the sandbox also denies writes to the file it points to, starting with the next command.
from line 548
548548 
549549If `git merge` or `git checkout` fails with `unable to unlink old` on one of these paths, see [A git command fails with `unable to unlink old`](#a-git-command-fails-with-unable-to-unlink-old).
550550 
551#### When the sandbox deletes `HEAD`, `objects`, or `refs`
552 
553After a sandboxed command ends, a newer top-level `HEAD`, `objects`, or `refs` entry in your working directory is deleted unless it's a folder with contents. Newer means the entry wasn't there when the sandbox last built its configuration: at session start, or after a change such as [`/cd`](https://code.claude.com/docs/en/commands).
554 
555A newer `objects` or `refs` folder with contents is deleted too, with its contents, when a `HEAD` file or symlink was there the last time the sandbox built its configuration. To keep a folder, see [A folder named `objects` or `refs` disappears after a command](#a-folder-named-objects-or-refs-disappears-after-a-command).
556 
551557#### Denied paths that pass through a symlink
552558 
553559On macOS, when a denied path passes through a symlink, such as a `~/.claude` that is a link into a dotfiles repository, the sandbox also denies the path's real location. This covers the sandbox's own denials and your literal `denyWrite` and `denyRead` entries.
from line 882
876882* Outside the directories the sandbox lets commands write to at all
877883 
878884After the failure, Claude may [offer to rerun the command outside the sandbox](#the-unsandboxed-retry-escape-hatch). Approve that retry, or run the git command yourself in another terminal. If you've set `allowUnsandboxedCommands` to `false`, Claude can't offer the retry, so run the command yourself.
885 
886### A folder named `objects` or `refs` disappears after a command
887 
888A top-level `objects` or `refs` folder that appeared during the session can be [deleted after a sandboxed command ends](#when-the-sandbox-deletes-head-objects-or-refs). It doesn't matter whether a command, Claude, or you created it. To keep a folder, leave something in it. If a `HEAD` file or symlink has been at the top level during the session, move it away, then restart every Claude Code session open there.
889 
890Before v2.1.307, the folder was deleted even with something in it and no `HEAD`. On an earlier version, [update Claude Code](https://code.claude.com/docs/en/setup.md#update-claude-code).
879891 
880892### Bubblewrap fails to start inside a container
881893 

self-hosted-environments Changed · +3 / -2 lines

from line 26
2626 
2727The two Claude Code boxes in the diagram are session processes: one runner executing two sessions at once, up to its configured capacity. A runner serves one [owner](#key-concepts) at a time and locks to that owner when it claims its first session, so checked-out code never mixes between owners; [Runner lifecycle](#runner-lifecycle) covers the rule.
2828 
29You can start runners yourself and keep them running, or run the [autoscaling orchestrator](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#on-demand-runners), a second process you host, which starts runners as sessions queue; each runner exits on its own when its work finishes. Either way, you set the environment up once, and it appears in the picker on every supported surface.
29You can start runners yourself and keep them running, or run the [autoscaling orchestrator](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#on-demand-runners), a second process you host, which starts runners as sessions queue. Either way, you set the environment up once, and it appears in the picker on every supported surface.
3030 
3131## Availability and limitations
3232 
from line 118
118118 
119119Corporate egress proxies are supported. The runner and the optional [autoscaling orchestrator](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#on-demand-runners) honor the proxy and mTLS environment variables described in [Network configuration](https://code.claude.com/docs/en/network-config.md), such as `HTTPS_PROXY` and `NO_PROXY`; set them in each process's environment. The variables cover control-plane calls, the orchestrator's [SCM connector](https://code.claude.com/docs/en/self-hosted-environments-reference.md#scm-connector-flags) WebSocket, and the built-in clone for HTTPS remotes, and sessions inherit them from the runner. Session streaming uses server-sent events over HTTPS, so a proxy in the path must not buffer responses.
120120 
121If your proxy also requires a `Proxy-Authorization` header, the runner can add it to each connection it opens to the proxy; see [Authenticate to an egress proxy](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#authenticate-to-an-egress-proxy).
121* **Proxy authentication**: if your proxy also requires a `Proxy-Authorization` header, the runner can add it to each connection it opens to the proxy; see [Authenticate to an egress proxy](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#authenticate-to-an-egress-proxy).
122* **Long-lived responses**: if a runner picks up a session but Claude never answers its first message, your proxy may be the cause. See [A session is picked up, but Claude never answers its first message](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#when-a-sessions-first-message-is-never-answered).
122123 
123124## What stays on your infrastructure
124125 

self-hosted-environments-configuration Changed · +62 / -6 lines

#### Fix re-opened sessions that stay Queued or show Failed ### Stop sessions that fail to fetch server-managed settings

from line 32
3232| `CLAUDE_CODE_REMOTE_SESSION_UUID` | The same session ID in canonical UUID form, for systems that key on UUIDs. |
3333| `CLAUDE_CODE_REMOTE_SLACK_THREAD_URL` | For a [Claude Tag](https://claude.com/docs/claude-tag/overview) session that belongs to one Slack thread, the link to that thread. Unset for other sessions, and can be unset for a thread session too. |
3434| `CLAUDE_CODE_REMOTE_SLACK_THREAD_TS` | For a Claude Tag session that belongs to one Slack thread, that thread's Slack timestamp, such as `1700000000.000100`. Can be unset, and can be set when `CLAUDE_CODE_REMOTE_SLACK_THREAD_URL` isn't, so check each variable on its own. |
35| `CLAUDE_CODE_REMOTE_SLACK_CHANNEL_ID` | For a Claude Tag session, the ID of the Slack channel or direct message that the session serves or that its thread is in, such as `C0123ABCDE`. A Slack timestamp identifies a message within one channel, so record this ID together with `CLAUDE_CODE_REMOTE_SLACK_THREAD_TS`. Unset for other sessions, and can be unset for a Claude Tag session too. |
3536| `CLAUDE_SESSION_INGRESS_TOKEN_FILE` | Absolute path to a per-session file holding the current session JWT, kept fresh across token refreshes. Shell subprocesses read it for their `Authorization` header when downloading attachments the user added to the session. `exec` preserves the variable automatically; a wrapper that rebuilds the child's environment must carry the variable over, or attachment downloads silently stop working. |
3637| `CLAUDE_CONFIG_DIR` | Per-session Claude config directory, written at session start from the snapshot of the runner host's config that the runner captures at startup; see [Permissions and tool approval](#permissions-and-tool-approval). Writes here are isolated to this session. The directory stays under `<base-dir>/_sessions/` after the session ends unless you start the runner with [`--remove-session-state`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags); see [Reuse a pre-warmed checkout](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#reuse-a-pre-warmed-checkout). |
3738| `CLAUDE_CODE_REMOTE_MCP_CONFIG` | Path to a file in `CLAUDE_CONFIG_DIR` holding the MCP server definitions that Anthropic's control plane delivered for this session. If your wrapper rebuilds the child's environment, carry this variable over. Without it, when the control plane updates the delivered servers, the child shuts down any stdio, HTTP, or SSE server your wrapper adds with [`--mcp-config`](https://code.claude.com/docs/en/cli-reference#cli-flags) that isn't in the update. Requires Claude Code v2.1.299 or later. |
from line 41
4041 
4142The wrapper also inherits the rest of the child's managed environment, including any server-provided environment variables. `exec` propagates all of it automatically; if your wrapper spawns the child another way, forward the full environment.
4243 
43`CLAUDE_CODE_REMOTE_SLACK_THREAD_URL` and `CLAUDE_CODE_REMOTE_SLACK_THREAD_TS` reach your wrapper or [`command` hook](#command). They also reach what the session runs, such as shell commands, git hooks, and Claude Code hooks. The `checkout`, `post-session`, and `spawn-runner` hooks don't receive them.
44You can read the `CLAUDE_CODE_REMOTE_SLACK_*` variables in your wrapper or [`command` hook](#command), and in what the session runs, such as shell commands, git hooks, and Claude Code hooks. The `checkout`, `post-session`, and `spawn-runner` hooks don't receive them.
4445 
4546### Give a default to variables that can be unset
4647 
47`CCR_SESSION_ACCOUNT_EMAIL`, `CLAUDE_RUNNER_CLIENT_PLATFORM`, `CLAUDE_CODE_REMOTE_SLACK_THREAD_URL`, and `CLAUDE_CODE_REMOTE_SLACK_THREAD_TS` can each be unset. If your script uses `set -u`, Bash stops with `unbound variable` when it expands one that's unset, so expand them with a default, such as `"${CCR_SESSION_ACCOUNT_EMAIL:-}"`.
48If your script uses `set -u`, expand `CCR_SESSION_ACCOUNT_EMAIL`, `CLAUDE_RUNNER_CLIENT_PLATFORM`, `CLAUDE_CODE_REMOTE_SLACK_THREAD_URL`, `CLAUDE_CODE_REMOTE_SLACK_THREAD_TS`, and `CLAUDE_CODE_REMOTE_SLACK_CHANNEL_ID` with a default, such as `"${CCR_SESSION_ACCOUNT_EMAIL:-}"`. Each can be unset, and under `set -u` Bash stops with `unbound variable` when it expands an unset one.
4849 
4950### Quote the arguments and variables your script expands
5051 
from line 139
138139| Variable | Description |
139140| :- | :- |
140141| `CLAUDE_RUNNER_REPO_URL` | Repository URL to clone, after any `--git-host-rewrite` and `--git-ssh-rewrite` have been applied |
141| `CLAUDE_RUNNER_REPO_REF` | Revision to check out, as the session requested it: a branch, tag, commit SHA, or full reference name such as `refs/pull/<number>/head`. Empty means the repository's default branch. |
142| `CLAUDE_RUNNER_REPO_REF` | Revision to check out, as the session requested it: a branch, tag, commit SHA, or full reference name such as `refs/heads/<branch>` or `refs/pull/<number>/head`. Empty means the repository's default branch. |
142143| `CLAUDE_RUNNER_CHECKOUT_PATH` | Absolute path where the working tree must be left |
143144| `CLAUDE_RUNNER_SESSION_ID` | Session ID in the tagged `session_...` form, for logging and correlation |
144145| `CLAUDE_RUNNER_SESSION_UUID` | The same session ID in canonical UUID form |
from line 320
319320 
320321The contract has four rules, whichever platform your hook provisions on:
321322 
3221. **Be idempotent on `CLAUDE_RUNNER_ORDER_ID`.** Redelivery of the same request must spawn at most one runner. Derive a deterministic resource name from the order ID and let your platform reject the duplicate. Don't key on `CLAUDE_RUNNER_SESSION_ID` instead. Every re-request for a session carries the same session ID with a new order ID, so a workload named or deduplicated by the session ID is created once and never again for that session.
3231. **Be idempotent on `CLAUDE_RUNNER_ORDER_ID`.** Redelivery of the same request must spawn at most one runner. Derive a deterministic resource name from the order ID and let your platform reject the duplicate. Don't key on `CLAUDE_RUNNER_SESSION_ID` instead. Every re-request for a session carries the same session ID with a new order ID. A workload named or deduplicated by the session ID isn't created again while the first one exists.
3233242. **Don't retry the workload.** One order ID means at most one created workload. If the runner never registers, Anthropic re-requests with a fresh order ID after `--expected-spawn-seconds`.
3243253. **Use the exit-code contract.** Exit with the status that matches the outcome:
325326 
326327 * **Exit 0**: submitted.
327 * **Exit 1, or still running when `--hook-timeout` ends**: retryable failure. The session backs off and is re-offered.
328 * **Exit 1, or still running when `--hook-timeout` ends**: retryable failure. The session backs off and is re-offered. After 10 failed requests in a row, it's blocked as it is after exit 2 or higher.
328329 * **Exit 2 or higher**: non-retryable failure. The session is blocked from spawning again until a user sends it a new message or an [Owner](https://code.claude.com/docs/en/cloud-environments.md#organization-shared-environments) selects **Retry** on it in the environment's **Activity** tab.
329330 
330331 While a session backs off, it shows **Queued** in the **Activity** tab with no spawn error, so read the hook's output in the orchestrator's log. Once blocked, the session shows **Failed**. Expand it to read the spawn error, taken from the last 512 characters of the hook's stderr. Write an actionable error to stderr, and no secrets. In a shell hook, [keep transient failures retryable](#keep-transient-failures-retryable-in-a-shell-hook).
from line 335
334335 
335336Everything the hook writes to stdout or stderr appears in the orchestrator's log with credentials automatically redacted. If sessions stay queued, check the orchestrator's `/healthz` body for queue counts, then open your environment's **Activity** tab on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments): expand a failed session there for its spawn error, and select **Retry** to re-request it.
336337 
337A session that stays queued with no spawn error in the **Activity** tab can mean the hook is keyed on the session ID. To confirm, check whether your platform has a workload for that session's first spawn request and none for the re-requests. If so, key the workload on `CLAUDE_RUNNER_ORDER_ID` instead.
338#### Fix re-opened sessions that stay Queued or show Failed
338339 
340If re-opened sessions stay **Queued** or show **Failed**, or your hook times out on them, one cause is a hook that names or deduplicates workloads by the session ID. A session is re-opened when its user sends it a message after its runner has exited. For example, take a hook that builds each workload's name like this:
341 
342```bash theme={null}
343name="runner-${CLAUDE_RUNNER_SESSION_ID}"
344```
345 
3461. At the session's first spawn request, the hook creates a workload with that name, and a runner starts
3472. The runner exits, for example after [`--release-idle-session-min`](#make-on-demand-runners-exit-when-idle), and the user later re-opens the session
3483. The session's next spawn request carries the same session ID and a new order ID, so the hook builds the same name
3494. If your platform still has the workload from the first request, because nothing [removed](#make-on-demand-runners-exit-when-idle) it when its runner exited, the hook creates no new workload and no runner starts
3505. Each later spawn request for the session ends the same way
351 
352Open your own hook and read what it does when the workload already exists, for example when your platform rejects the duplicate name. Find that behavior in the first column to see what the re-opened session shows in the **Activity** tab:
353 
354| When the workload exists, your hook | **Queued** changes to **Failed** | Spawn error |
355| :- | :- | :- |
356| Exits 0 without creating anything | After 10 such requests in a row | `runner did not register within ETA after max attempts` |
357| Exits 1 | After 10 such requests in a row | Your hook's stderr tail, or `exit 1` if it wrote nothing to stderr |
358| Exits 2 or higher | Right away | Your hook's stderr tail, or `exit` and the code, such as `exit 2`, if it wrote nothing to stderr |
359| Waits until `--hook-timeout` ends | After 10 such requests in a row | Your hook's stderr tail, or `spawn-runner hook timed out after 60000ms` if it wrote nothing to stderr |
360 
361In the last row's spawn error, `60000` is your `--hook-timeout` in milliseconds. When a hook waits until `--hook-timeout` ends, the orchestrator's log also gets a `timed out after` line.
362 
363To fix the hook, build each name or key from `CLAUDE_RUNNER_ORDER_ID`, as [rule 1 of the hook contract](#the-spawn-runner-hook) requires. In the example, the name becomes:
364 
365```bash theme={null}
366name="runner-${CLAUDE_RUNNER_ORDER_ID}"
367```
368 
369If your hook waits, also make it exit once it has submitted the workload, because a waiting hook [delays its orchestrator's next hooks](https://code.claude.com/docs/en/self-hosted-environments-reference.md#orchestrator-cli-flags). After you change the hook, check each stuck session's status in the **Activity** tab:
370 
371* **Queued**: the session gets its next spawn request on its own
372* **Failed**: select **Retry** on it as an Owner, or have its user send it a new message
373 
339374#### Make on-demand runners exit when idle
340375 
341376By default, a runner your hook starts doesn't exit when its session's turn finishes, so the workload you submitted keeps running. To make runners exit, have your hook pass these [runner flags](https://code.claude.com/docs/en/self-hosted-environments-reference.md#runner-cli-flags):
from line 790
755790Outside [Claude Tag](https://claude.com/docs/claude-tag/overview) sessions, a session in a self-hosted environment runs with [auto memory](https://code.claude.com/docs/en/memory.md#auto-memory) off by default. For instructions that should carry across sessions, use the `CLAUDE.md` in your runner image or in the repository.
756791 
757792The runner's snapshot of the host's `~/.claude/` leaves out the `projects/` directory. Auto memory's default storage location is under that directory. If you put memory files there, the runner doesn't seed them into sessions, and they don't turn auto memory on.
793 
794### Stop sessions that fail to fetch server-managed settings
795 
796To keep a session that fetches [server-managed settings](https://code.claude.com/docs/en/server-managed-settings.md) from starting when that fetch fails, set [`forceRemoteSettingsRefresh`](https://code.claude.com/docs/en/settings-reference#forceremotesettingsrefresh) to `true` in the [`managed-settings.json`](https://code.claude.com/docs/en/settings#where-settings-live) in your runner image. A reply from Anthropic's API that your organization has no server-managed settings counts as a successful fetch.
797 
798Before you add the key, check whether the environment runs Claude Tag channel sessions or sends model requests to Bedrock or Agent Platform:
799 
800* **Claude Tag channel sessions**: leave the key out on runners in an environment that an Owner [routes Claude Tag channels to](https://code.claude.com/docs/en/cloud-environments.md#set-the-environment-a-claude-tag-channel-uses). Anthropic's API refuses these sessions' request for server-managed settings, so with the key set they fail to start. Put their rules in the runner image's `managed-settings.json`, which [they still read](https://code.claude.com/docs/en/managed-settings.md#where-and-when-a-policy-applies). To use the key for your other sessions, run those in a separate environment.
801* **Bedrock or Agent Platform**: sessions on these providers [don't fetch server-managed settings](#what-differs-from-sessions-on-the-anthropic-api), so they start whether or not you set the key. Enforce your rules from the same `managed-settings.json` instead.
802 
803This `managed-settings.json` sets the key:
804 
805```json managed-settings.json theme={null}
806{
807 "forceRemoteSettingsRefresh": true
808}
809```
810 
811Set the key in that file even if you also set it in the claude.ai admin console. A new session has no [cached copy](https://code.claude.com/docs/en/server-managed-settings.md#fetch-and-caching-behavior) of your server-managed settings, so if you set the key only in the console, the session doesn't have it yet. If its fetch then fails, the session runs without those settings until a later fetch succeeds.
812 
813To confirm that a failed settings fetch is why a session didn't start, look at [Claude Code's last output](#keep-stdin-and-file-descriptor-3-attached) in the session or the runner's log. It includes `Your organization requires remote managed settings to load, but they could not be loaded.`
758814 
759815### Repository settings in sessions with several repositories
760816 

self-hosted-environments-deploy Changed · +169 / -4 lines

### Add github.com repositories to Claude Tag channel sessions ### Give each session its own memory limit ### Leave room under process and thread limits #### Where to set each limit #### Measure one session's peak #### Check a `pids.max` limit ### Confirm that the runner uses your clone ### Release idle sessions on a fixed fleet ### A session is picked up, but Claude never answers its first message #### Confirm the cause in the runner's log #### Stop the session from trying again

from line 309
309309 
310310For each session that uses Anthropic-managed git, the runner also logs a `[runner:warn]` line that begins `governed git: GIT_SSL_CAINFO is set` or `governed git: GIT_SSL_NO_VERIFY is set`. The line says what the runner did with that variable for its own git, for git inside the session, and for your lifecycle hooks. It ends with whether you need to change anything.
311311 
312### Add github.com repositories to Claude Tag channel sessions
313 
314If Claude can't add a github.com repository to a Claude Tag channel session, make sure the channel has access to it, even when your runner can clone it. That access comes from settings on GitHub and in your Claude organization, not from your runner's git credentials.
315 
316**1. Install the Claude GitHub App**
317 
318Install the [Claude GitHub App](https://github.com/apps/claude) on the GitHub organization or account that owns the repository, and include that repository in the installation.
319 
320**2. Link the GitHub organization or account**
321 
322In [**Organization settings > Git providers**](https://claude.ai/admin-settings/source-control), link that organization or account to your Claude organization. An Owner of your Claude organization who also owns the GitHub organization can link it. See [Link your GitHub organization](https://claude.com/docs/claude-tag/admins/configure-github#link-your-github-organization).
323 
324**3. Grant the repository**
325 
326In [Claude Tag admin settings](https://claude.ai/admin-settings/claude-tag), grant the repository to the channel or to a wider scope that includes the channel: its Slack workspace or all of Slack. Someone with the Owner role or the Claude Tag Admin permission can grant it. A channel manager can also add it to their own channel if their GitHub account is an admin of the repository. See [Grant repository access](https://claude.com/docs/claude-tag/admins/configure-github#grant-repository-access).
327 
328Then start a new thread in the channel and [name the repository](https://claude.com/docs/claude-tag/admins/configure-github#how-granted-repositories-reach-a-session) in your first message.
329 
330If Claude still can't add the repository, see [If Claude can't reach a repository](https://claude.com/docs/claude-tag/admins/configure-github#if-claude-can%E2%80%99t-reach-a-repository) for where to check the GitHub link and the repository grant.
331 
312332### Rewrite git URLs for private networks
313333 
314334Repository URLs arrive from the control plane as HTTPS, with the hostname of your git host; for GitHub Enterprise, that's the hostname you configured for the [GitHub Enterprise integration](https://code.claude.com/docs/en/github-enterprise-server.md) on claude.ai. Two repeatable flags rewrite those URLs before clone:
from line 392
372392 
373393Builds and tests are usually the largest and most variable part of a session's load, so run a representative build of your repository, measure its peak CPU and memory, and raise any starting value that leaves no room for the Claude Code process on top of that peak.
374394 
375The runner uses `--capacity` to cap how many sessions it runs at once. It doesn't divide CPU or memory between them, so the sessions on a runner share the container's CPU and memory. To cap one session's share, apply limits from your [wrapper script](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#wrapper-scripts). What to give one container therefore depends on how many sessions it serves at once:
395The runner uses `--capacity` to cap how many sessions it runs at once. It doesn't divide CPU or memory between them, so the sessions on a runner share the container's CPU and memory. To keep one session's memory use from ending the others, run one session per runner with [its own memory limit](#give-each-session-its-own-memory-limit).
376396 
377397* **One session per runner**: give each container one session's values. Use this sizing at `--capacity 1`, which the [hardening section](#harden-your-deployment) recommends, and for [on-demand runners](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#on-demand-runners), where you set the values on the workload your [`spawn-runner` hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#the-spawn-runner-hook) submits, such as a Kubernetes Job's pod template.
378398* **Several sessions per runner**: at a `--capacity` above one, multiply one session's values by the capacity, because up to that many sessions can run in the container at the same time. The [Kubernetes](#kubernetes) and [Docker Compose](#docker-compose) recipes run `--capacity 4` with no CPU or memory limits, so add limits sized for the capacity you run.
399* **Several runner containers on one host**: size the host for the sum of the runners' capacities, because each runner counts only its own sessions against `--capacity`:
379400 
401 * **Memory**: multiply one session's memory by the sum, then add what the host's own processes use
402 * **CPU**: multiply one session's CPU request by the sum. To let every session burst at once, multiply its CPU limit instead.
403 
404 Give each runner its own container. Use the [same `--base-dir` path](#keep-the-base-directory-and-capacity-identical-across-runners) in each, on storage that no other runner's container mounts, so that each runner has [its own working directory](#harden-your-deployment).
405 
406### Give each session its own memory limit
407 
408To stop one session from ending the others when memory runs out, run each runner at `--capacity 1` under its own memory limit, set to one session's memory:
409 
410* **In a container**: run one runner per container, and set the container's memory limit, such as `limits.memory` on Kubernetes, `--memory` on Docker, or `mem_limit` in a Compose file. Keep the sum of the containers' limits below the host's memory, because when the host itself runs out, the kernel can kill processes anywhere on the host.
411* **Under systemd without a container**: set `MemoryMax` on the runner's unit, and run one runner per host, because runners that [use the same `--base-dir`](#keep-the-base-directory-and-capacity-identical-across-runners) on one filesystem share the repositories cloned there. For example, at `--capacity 1` with no `checkout` hook, their sessions on one repository can work in the same checkout at once. To run several runners on one host, give each a container.
412 
413When a container or unit reaches its memory limit, the kernel kills processes only inside it. If you set `--capacity` above one, the sessions share one limit. When they reach it, the kernel doesn't choose by session which processes to kill, so one session's memory use can end the others.
414 
415On Kubernetes, you can't separate the sessions with either value of the kubelet's `singleProcessOOMKill` setting. You can't cap a session's total memory with a memory `ulimit` in your [wrapper script](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#wrapper-scripts) either. The kernel applies a value such as `ulimit -v` to each process separately.
416 
417### Leave room under process and thread limits
418 
419To size a process and thread limit, measure what one session needs and multiply it by the number of sessions that share the limit. When those sessions reach the limit, the kernel refuses new processes and threads, so commands, builds, and tests in any of them can fail. The refused call returns `EAGAIN`, whose standard message is `Resource temporarily unavailable`.
420 
421Suppose one session peaks at 300 processes and threads, and four sessions share the limit. Together they need 1,200, and with a limit of 2,400, you leave room for builds that need twice as much.
422 
423#### Where to set each limit
424 
425Each limit in this table counts threads as well as processes, and `pids.max` is the file that holds a cgroup's limit:
426 
427| Limit | Where you set it | What shares it |
428| :- | :- | :- |
429| `pids.max` on Kubernetes | `podPidsLimit` in the kubelet configuration of the nodes that run runners. You can't set it in a Pod spec. | The containers of one Pod |
430| `pids.max` on Docker | `--pids-limit`, or `pids_limit` in a Compose file | One container |
431| `pids.max` under systemd | `TasksMax` on the runner's unit | One unit |
432| `ulimit -u` | The shell or service that starts the runner | Everything on the host that runs as the runner's user ID. Not enforced for root. |
433 
434When you don't set `TasksMax`, systemd applies `DefaultTasksMax`, which defaults to 15% of the lowest of `kernel.pid_max`, `kernel.threads-max`, and the root cgroup's `pids.max`.
435 
436#### Measure one session's peak
437 
438To get the numbers for your own runners, measure one session on its host, which on Kubernetes is the node:
439 
440**1. Start one runner alone**
441 
442Start one runner at `--capacity 1` with nothing else in its container or systemd unit, alone on its host and in its environment:
443 
444* **Its host**: use one that runs no other runner. You can then pick out its process in the next step.
445* **Its environment**: make it the only runner there, for example in an environment you [create for the test](https://code.claude.com/docs/en/self-hosted-environments-quickstart.md#set-up-manually). Sessions are routed to an environment and not to a runner.
446 
447Don't start a session yet, because you need the directory from the next step before the build starts.
448 
449**2. Find the runner's cgroup directory**
450 
451On the host, not inside the container, find the runner's process ID, for example with `ps`. Read `/proc/<pid>/cgroup` for that ID:
452 
453* **A line contains `:pids:`**: the host uses cgroup v1, and the directory is `/sys/fs/cgroup/pids` followed by the text after `:pids:`
454* **The only line starts with `0::`**: the host uses cgroup v2, and the directory is `/sys/fs/cgroup` followed by the text after `0::`
455 
456**3. Run a representative build and record the peak**
457 
458Run one session through a representative build, and take the peak from the runner's cgroup directory:
459 
460* **The directory has `pids.peak`**: read the file after the build finishes
461* **The directory has no `pids.peak`**: read `pids.current` once a second while the build runs, and keep the highest value. A peak between two reads goes unseen, so treat that value as a low estimate.
462 
463**4. Multiply by the sessions that share the limit**
464 
465* **One runner's Pod, container, or unit**: multiply the peak by the runner's `--capacity`
466* **A limit that several runners share**: multiply the peak by the sum of their capacities
467 
468Then add the peak of anything else that shares the limit, such as another container in the Pod.
469 
470**5. Set the limit above the result**
471 
472With a higher limit, you leave room for heavier builds, and with a lower one, you stop a fork bomb sooner.
473 
474#### Check a `pids.max` limit
475 
476* **Check whether `pids.max` was reached**: search the host's kernel log, for example with `dmesg`, for a line that contains `cgroup: fork rejected by pids controller in` and a cgroup's path. The kernel logs it only the first time it refuses a process or thread in that cgroup, so search the whole log.
477* **See the limit in force**: read `pids.max` in the runner's cgroup directory and in each directory above it. The lowest value is the limit in force, and `max` means no limit at that level.
478 
380479## Kubernetes
381480 
382481The runner serves `GET /healthz` on port 8080 by default, configurable with `--health-port`, so Kubernetes probes work with no extra setup. The endpoint returns `200` whenever the process is alive, so the probes below detect a dead process, not a stuck one; to catch a runner that stopped polling, alert on the `last_poll_age_seconds` series from [`/metrics`](https://code.claude.com/docs/en/self-hosted-environments-reference#prometheus-metrics). The Deployment below mounts the environment secret from a Kubernetes Secret, points the liveness and readiness probes at `/healthz`, and sets a 90-second termination grace period. See [Shutdown timing](#shutdown-timing) for why the grace period matters.
from line 672
573672 
574673What the reuse path does and doesn't guarantee:
575674 
576* **Any clone shape works**: a full, shallow, or single-branch clone at the path is used as-is. The runner never passes `--depth` when fetching into an existing clone, so a full pre-warm keeps its full history and a shallow one stays shallow. `CLAUDE_RUNNER_FETCH_DEPTH` (`full`, `0`, or a number; default 50) controls only the cold clone the runner makes when no clone exists yet.
675* **Clone depth**: build the clone without `--depth`, with or without `--single-branch`. The runner never passes `--depth` when it fetches into a clone with an `origin` remote, even if you set [`CLAUDE_RUNNER_FETCH_DEPTH`](https://code.claude.com/docs/en/self-hosted-environments-reference#environment-variable-only-settings). With a full clone, each session downloads only the commits the clone lacks. With a shallow clone, a session on a branch that forked before the clone's oldest commit downloads that branch's whole history.
577676* **Tracked changes reset, untracked files persist**: at `--capacity 1`, each session starts from a hard reset that wipes the previous session's tracked modifications, but the runner never runs `git clean`, so untracked files from the locked owner's earlier sessions stay in the tree.
578677* **Per-session directories persist too**: alongside the checkout, the runner creates per-session entries under `<base-dir>/_sessions/` for every session it runs. The session's Claude config directory holds a local copy of the conversation transcript. Next to it sit the session's uploaded files, when the session has any. The session directory sits there too: it holds any per-session worktrees and `checkout` hook checkouts while the session runs, and it keeps whatever else Claude wrote in it.
579678 
from line 682
583682* **With the git proxy, a `reftable` pre-warm loses its refs**: when the runner sanitizes `.git/` before each session, it keeps `.git/refs/` and removes the `.git/reftable/` directory. A clone pre-warmed in git's `reftable` ref format loses its refs that way, so pre-warm the clone in git's `files` ref format instead.
584683* **Long clones need no workaround**: the runner bounds each git operation with a 120-second no-progress watchdog and a 30-minute hard cap, not a flat timeout, so a slow cold clone that keeps reporting progress completes.
585684 
685### Confirm that the runner uses your clone
686 
687To confirm that the runner uses your clone, start it with [`--log-level debug`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) and start a session on the repository. The runner logs `Prefetched repo found at` and the clone's path. If it logs `No prefetched repo, fresh init at` instead, it found no usable clone at the path in that line. Check that your clone is at that path, owned by the runner's user, with a remote named `origin`.
688 
689If the runner finds your clone and sessions still start slowly, check whether the clone is shallow.
690 
586691## Pin the version
587692 
588693Each session's child process runs the runner's own binary by default, and the runner turns off auto-update inside the sessions it spawns. With that default, every session runs the version you installed on the host or built into the image. A host-level update takes effect the next time the runner starts.
from line 710
605710* **Fixed fleet**: run a static set of runner replicas and scale on the [Prometheus metrics](https://code.claude.com/docs/en/self-hosted-environments-reference.md#prometheus-metrics) each runner serves
606711* **On-demand runners**: run the `claude self-hosted-runner orchestrator` subcommand, which polls Anthropic for sessions that are queued with no runner available and invokes your `spawn-runner` hook to boot one per session. See [On-demand runners](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#on-demand-runners).
607712 
713### Release idle sessions on a fixed fleet
714 
715If sessions stay queued on a fixed fleet while every runner is online, idle sessions may be the cause. By default, a session keeps its slot after its turn finishes, and its runner stays locked to that session's [owner](https://code.claude.com/docs/en/self-hosted-environments#key-concepts). If your team's [cloud scheduled task](https://code.claude.com/docs/en/routines.md) starts a new session at each run, its idle sessions can fill every slot.
716 
717To check, read `claude_code_self_hosted_runner_session_idle_seconds` in each runner's [Prometheus metrics](https://code.claude.com/docs/en/self-hosted-environments-reference.md#prometheus-metrics). Each session a runner holds has its own series: `0` while the session is busy, otherwise the seconds since it went idle. Choose the idle time you'd allow: longer than the pauses people take mid-task, and short enough that each run is released before the scheduled task's next one starts. Compare the series with it.
718 
719* **Series above that time, such as `1800` for 30 minutes**: idle sessions hold those slots, so set `--release-idle-session-min`
720* **Every series below it**: no session has been idle that long. Add runners, or set [`--kill-session-after-min`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) for [sessions that never go idle](#some-sessions-don’t-count-as-idle).
721* **A runner with no series**: no session holds it. See **Sessions stay queued** under [Troubleshooting](#troubleshooting).
722* **A runner whose metrics don't answer**: its process may have exited. See **An environment that looks healthy** under [When the runner exits](#when-the-runner-exits).
723 
724To release idle sessions, start every runner with [`--release-idle-session-min`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) and that time in minutes, such as `--release-idle-session-min 30`. The runner then releases any session that stays idle that long, whether a scheduled task or a person started it.
725 
726* **Unpushed work**: a released session resumes at its next message, without the work it hadn't pushed. To keep its commits, see [Resumed sessions lose unpushed work](#additional-limitations).
727* **Runner exits**: a runner that releases its last session starts a [normal exit](#when-the-runner-exits). Have your supervisor restart it on a fresh filesystem, as [Harden your deployment](#harden-your-deployment) describes.
728* **Sessions that never go idle**: also set [`--kill-session-after-min`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags), because [some sessions don't count as idle](#some-sessions-don’t-count-as-idle)
729 
608730## Known issues and limitations
609731 
610732The following are the limitations in this release, with workarounds where one exists.
from line 760
638760### Additional limitations
639761 
640762* **Resumed sessions lose unpushed work**: a fresh runner clones the repository again from its starting branch, so work the session hadn't pushed is gone.
641 * **To keep committed work**: set [`--push-outcome-on-release`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) on every runner in the environment, because a runner without the flag resumes the session from its starting branch. A runner with the flag makes a best-effort push of the session's outcome branches before it releases, and the resumed session starts from those commits. The push uses the runner host's own git credentials, including on a runner that uses [Anthropic-managed git](#use-the-anthropic-git-proxy). Uncommitted changes are still lost.
763 * **To keep committed work**: set [`--push-outcome-on-release`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) on every runner in the environment, because a runner without the flag resumes the session from its starting branch. A runner with the flag makes a best-effort push of the session's outcome branches before it releases, and the resumed session starts from those commits. The push uses the runner host's own git credentials, including on a runner that uses [Anthropic-managed git](#use-the-anthropic-git-proxy).
642764 * **With a `checkout` hook**: repositories checked out via a [`checkout` lifecycle hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#checkout) aren't pushed. Snapshot those from the [`post-session` hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#post-session) instead.
643765 * **Before enabling the flag**: restrict who can push to `claude/*` refs on the source remote. On resume, the runner fetches the previously pushed branch without verifying who pushed it.
766 * **Uncommitted changes are still lost**: to prompt Claude to commit and push when it finishes responding, see [Prompt sessions to push their work](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#prompt-sessions-to-push-their-work)
644767* **A repository added mid-session can fail to clone**: Claude clones it with `git clone` over HTTPS. On a runner without [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), the clone fails with a git authentication error if nothing on the host can read the repository. Where you can, select every repository the session needs when you create it.
645768* **Some connectors don't appear in self-hosted sessions**: a connector you haven't yet connected in claude.ai Settings isn't listed in a self-hosted session, and the session won't prompt you to connect it. Connect it in Settings first, then start a fresh session. Adding a connector to an already-running session also doesn't make its tools available to Claude; start a fresh session to pick up a newly added connector.
646769* **When someone archives or deletes a session, or releases its runner from the session's menu**: don't count on the [`--push-outcome-on-release`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) push. To save commits in those cases, push `HEAD` to a rescue branch from a [`post-session` hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#post-session). If you start from the example there, make it push even when nothing is uncommitted, because it pushes only after a snapshot commit.
from line 803
680803* **Sessions fail immediately after pickup**: open the session in claude.ai/code to see the error. If the error shows that git couldn't authenticate, check the runner's [git credentials](#configure-git). On a runner started with `--use-anthropic-git-proxy`, see [When sessions fail to start on a runner with the git proxy](#when-anthropic-doesnt-serve-a-session).
681804 
682805 An unwritable base directory stops the runner at startup instead of failing sessions. See the **Runner exits at startup with `cannot create or write to base directory`** entry in this list.
806* **A session is picked up, but Claude never answers its first message**: if your runners reach `api.anthropic.com` through a proxy, configure it to pass the session's `/worker/events/stream` response through as it arrives. Raising the runner's timeouts doesn't help. To confirm the cause in the runner's log, see [A session is picked up, but Claude never answers its first message](#when-a-sessions-first-message-is-never-answered).
683807* **Sessions fail to start on a runner that set `--use-anthropic-git-proxy`**: look in the runner's log for `access denied by the git proxy`, or for a git error that names an `api.anthropic.com` address containing `/git_proxy/`. To tell whether Anthropic served the session and fix the cause, see [When sessions fail to start on a runner with the git proxy](#when-anthropic-doesnt-serve-a-session).
684808* **Sessions can't reach the network through an authenticating egress proxy**: when the source you set with [`--proxy-authorization-command` or `--proxy-authorization-file`](#authenticate-to-an-egress-proxy) fails, times out after 30 seconds, or yields an empty value, the runner answers that connection `502 Bad Gateway` and logs why. The runner redacts the command's stderr in that log and never logs the header value. With `--proxy-authorization-command`, run the command yourself on the host to confirm it prints the whole header value on stdout. If the runner instead exits at startup with `could not start the proxy-authorization listener`, it couldn't open its loopback listener.
685809* **Runner logs `Poll failed` lines containing `rejecting the malformed poll response`**: the runner received a work-poll response whose body isn't the queue's expected JSON. One cause is an intercepting proxy or a captive portal answering with its own page. If the runner connects through a proxy, configure the proxy to pass responses from `api.anthropic.com` through unaltered.
from line 828
704828* **A session fails to start with `This runner couldn't read`**: the runner asked the git host for a repository with its [git credentials](#configure-git) and got a not-found answer. The repository doesn't exist, or those credentials can't reach the repository.
705829 * **If you administer the runner**: give its git credentials read access to the repository, then start a new session.
706830 * **If you started the session**: if the repository doesn't exist, start a new session with the right repository. If the runner can't reach the repository, ask the runner's administrator to grant access, then start a new session.
707* **Sessions take minutes to start**: the initial clone usually dominates. Watch the `claude_code_self_hosted_runner_session_init_duration_seconds` [metric](https://code.claude.com/docs/en/self-hosted-environments-reference.md#prometheus-metrics) to confirm, and cut the clone with a [pre-warmed checkout](#reuse-a-pre-warmed-checkout) or a smaller `CLAUDE_RUNNER_FETCH_DEPTH`.
831* **Sessions take minutes to start or resume**: one cause is a fresh clone. On a fleet that starts each runner on an empty disk, with no [`checkout` hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#checkout), the runner clones the session's repositories afresh before it starts Claude Code. A session that resumes on an empty disk waits for the same clone again.
832 
833 To skip the fresh clone, supply a [pre-warmed checkout](#reuse-a-pre-warmed-checkout). If you already supply one, [confirm that the runner uses it](#confirm-that-the-runner-uses-your-clone). Where the runner makes the clone itself and exits after one session, lower [`CLAUDE_RUNNER_FETCH_DEPTH`](https://code.claude.com/docs/en/self-hosted-environments-reference#environment-variable-only-settings) from its default of 50 commits to fetch less history.
834 
835 If you use a hook, it runs in place of the runner's clone, so look at what your hook does instead. Watch the `claude_code_self_hosted_runner_session_init_duration_seconds` [metric](https://code.claude.com/docs/en/self-hosted-environments-reference.md#prometheus-metrics) to see how long starts take.
708836* **Turns fail with a 401**: when a turn ends with a 401 or 403 from the Anthropic API, the runner fetches a fresh [`CLAUDE_CODE_OAUTH_TOKEN`](https://code.claude.com/docs/en/self-hosted-environments-configuration#wrapper-scripts) from Anthropic and passes it to the session. The failed turn isn't retried. This token is short-lived, and the runner rotates it over the session's stdin.
709837 
710838 When a fetch fails, the runner logs an `inference_token refresh failed` line that says when it will retry, and it keeps retrying for as long as the session runs.
from line 911
783911* **The environment's Activity tab**: open the environment and select **Activity**. If new runners keep appearing there and none picks up work, your supervisor is restarting the runner.
784912 
785913For guided diagnosis on the runner host, run the [doctor subcommand](#troubleshooting).
914 
915### A session is picked up, but Claude never answers its first message
916 
917If a runner picks up a session but Claude never answers the first message, and your runners reach `api.anthropic.com` through a proxy, the proxy may be the cause. Anthropic sends a session its messages over one streaming response that stays open. If your proxy buffers it, or ends it before passing anything on, the first message never arrives. Other requests from the session still succeed.
918 
919To fix it, change how the proxy handles this request:
920 
921```text theme={null}
922GET https://api.anthropic.com/v1/code/sessions/<session-id>/worker/events/stream
923```
924 
925* **Don't buffer the response**: pass it through as it arrives. Its content type is `text/event-stream`.
926* **Let the response stay open**: it can stay open for up to an hour. If the proxy has an idle timeout, keep it well above 15 seconds, because Anthropic sends a keep-alive at least that often by default.
927 
928Then start a new session and check that Claude answers. Raising the runner's timeouts doesn't help: you only wait longer before the runner stops the session.
929 
930#### Confirm the cause in the runner's log
931 
932These steps are for runners on Claude Code v2.1.287 or later, at the default `--log-level info`. Older runners log these lines later, or not at all.
933 
9341. Wait for [`--startup-timeout-min`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) to pass, 15 minutes by default. The runner then stops the session and logs a line that contains `no child output for` and ends `— releasing`.
9352. Search the runner's log for `SDKStartup: SSE`. These lines follow a `finished: interrupted` line, a few lines after the one in step 1. Most have no timestamp, so a log collector may show them elsewhere.
9363. Find the line in this table:
937 
938| Log line contains | What to do |
939| :- | :- |
940| `SSE connect failed (no response in 30s)` or `SSE liveness timeout` | Check that the proxy forwards the request, passes the response's headers and data through as they arrive, and doesn't drop the connection |
941| `SSE connect failed (HTTP` and a status code | Check the proxy's own log. If the proxy answered the request itself, let the request through it. |
942| `SSE connect failed (` and any other text | The text in the parentheses is the error. Look for the same connection in the proxy's own log. |
943| `SSE stream ended by server`, again and again | Check the proxy's time limit for long-lived responses. A single such line is normal. |
944| `SSE reconnected after` | Look for one of the other lines after it. This one doesn't mean that anything reached the session. |
945 
946If the log has none of these lines, or the proxy's log shows nothing wrong, [report an issue](#report-an-issue).
947 
948#### Stop the session from trying again
949 
950The session keeps the message pending, and a runner that can serve the session's owner picks the session up again. Until you change the proxy, the session waits the same way each time. To stop that, archive the session in claude.ai/code.
786951 
787952## What's next
788953 

settings-reference Changed · +36 / -8 lines

##### Where wildcards work in sandbox paths ##### Directory names with brackets, braces, or parentheses

This page is larger than the 256 KiB this site keeps, so one side of the diff below stops where the stored text does.

from line 320
320320}
321321```
322322 
323A model ID entry such as `"claude-opus-5"` also permits later versions that extend it, such as Opus 5.5. To block one of those versions, use [`deniedModels`](#deniedmodels). To make each model ID entry permit only the version it names, use [`availableModelsMatch`](#availablemodelsmatch). See [Restrict model selection](https://code.claude.com/docs/en/model-config.md#restrict-model-selection).
323A model ID entry such as `"claude-opus-5"` also permits later versions that extend it, such as Opus 5.5. To block one of those versions, use [`deniedModels`](#deniedmodels). To make each model ID entry permit only the version it names, use [`availableModelsMatch`](#availablemodelsmatch). See [Restrict model selection](https://code.claude.com/docs/en/model-config.md#restrict-model-selection). For third-party provider IDs, see [Pin models for third-party deployments](https://code.claude.com/docs/en/model-config.md#pin-models-for-third-party-deployments).
324324 
325325### `availableModelsMatch`
326326 
from line 329
329329* **Scope**: [`Managed`](#scopes). Claude Code ignores the key in user, project, and local settings and in `--settings`, with a warning
330330* **Type**: string, one of:
331331 * `"prefix"`: a model ID entry permits its version and any model ID that extends it with another segment
332 * `"exact"`: a model ID entry permits only the version it names, including that version's dated IDs, so `"claude-opus-5"` permits Opus 5 but not `claude-opus-5-5`. A family alias such as `"opus"` still permits the whole family, and `best`, `opusplan`, and `default` entries are ignored
332 * `"exact"`: a model ID entry permits only the version it names, so `"claude-opus-5"` permits Opus 5 but not `claude-opus-5-5`. An entry such as `"claude-opus-4-5"` also permits its dated Anthropic model ID `claude-opus-4-5-20251101`. A family alias such as `"opus"` still permits the whole family, and `best`, `opusplan`, and `default` entries are ignored
333333* **Default**: `"prefix"`
334334 
335335This example permits Opus 5 and Sonnet 5 and no later release of either:
from line 1367
13671367 
13681368Claude Code strips a trailing slash from a directory path, so `~/.aws` and `~/.aws/` match the same directory. Before v2.1.224, Claude Code passed the trailing slash through to the sandbox, and Claude could still read or write paths under a `denyRead` or `denyWrite` entry written with one.
13691369 
1370Claude Code also removes a trailing `/**`, so `~/build/**` and `~/build` cover the same directory. Whether a wildcard such as `*` works depends on which list the entry is in and on the platform:
1370##### Where wildcards work in sandbox paths
13711371 
1372* **`allowWrite` and `denyWrite`**: on macOS, wildcards work. On Linux and WSL2, the sandbox mounts concrete paths, so Claude Code skips an entry that contains `*`, `?`, or `[` once the trailing `/**` is removed, and that entry has no effect. Claude Code adds the paths from your `Edit` permission rules to these lists, so the same limit applies to them, and the **Config** tab of `/sandbox` warns about `Edit` and `Read` permission rules that contain wildcards.
1373* **`denyRead` and `allowRead`**: wildcards work on every platform. On Linux and WSL2, Claude Code expands a read entry to the concrete paths it matches, which it doesn't do for the write lists.
1372The wildcards `*`, `?`, and `**` work in all four `sandbox.filesystem` path lists on macOS and in fewer places on Linux and WSL2:
13741373 
1375Braces match as ordinary characters in a name, so `~/data/{keys,tokens}` covers a directory literally named `{keys,tokens}`, not two alternatives. To cover `~/data/keys` and `~/data/tokens`, add each as its own entry.
1374| `sandbox.filesystem` list | What works on Linux and WSL2 | Example that doesn't work on Linux and WSL2 | What to write instead |
1375| :- | :- | :- | :- |
1376| `denyRead`, `allowRead` | A trailing `/**` works, so `~/build/**` covers the same directory as `~/build`. Any other wildcard works only after the first directory name of the entry's [full path](#sandbox-path-prefixes). | `/**/*.pem` in `denyRead` covers nothing, so it blocks no reads | `/srv/**/*.pem`, with one entry for each top-level directory |
1377| `allowWrite`, `denyWrite` | Only a trailing `/**` works, so `~/build/**` covers the same directory as `~/build` | `./src/*` covers only a file or directory in `src` that is itself named `*`, and nothing when there is none | `./src`, which covers everything under `src`, or one entry for each path |
13761378 
1379In `~/keys/*.pem`, the wildcard comes after the first directory name, which is `home` for a home directory of `/home/alice`:
1380 
1381```text theme={null}
1382~/keys/*.pem the entry
1383/home/alice/keys/*.pem its full path
1384 ^^^^ the first directory name, home
1385```
1386 
1387On Linux and WSL2, the **Config** tab of `/sandbox` in a session warns about `Edit` permission rules that contain a wildcard other than a trailing `/**`. Those wildcards don't work for sandboxed commands. Add each path the rule should cover, or the directory that holds them, to `allowWrite` for an allow rule or `denyWrite` for a deny rule.
1388 
1389##### Directory names with brackets, braces, or parentheses
1390 
1391To allow or block a directory whose name contains brackets, write the name as it is, such as `./app/[slug]`. In that entry, `[slug]` is the directory's real name, brackets included:
1392 
1393```text theme={null}
1394app/
1395└── [slug]/
1396 └── page.tsx
1397```
1398 
1399That entry covers everything under the directory, so leave off a trailing `/**`. On macOS and on Linux and WSL2, the `app/[slug]` directory has to exist when the sandboxed command starts.
1400 
1401* **Brackets such as `[slug]`**: in every list on macOS, and in `denyRead` and `allowRead` on Linux and WSL2, the brackets in `./app/[slug]` are also a pattern. The entry then also matches any path under `app` named with one character from `slug`, such as `app/s` or `app/g`. With that entry in `allowRead`, or in `allowWrite` on macOS, sandboxed commands can reach those paths as well. Rename any they shouldn't reach.
1402* **Brackets such as `[user-id]` or `[post-id]`, where the hyphen's left character sorts after its right**: `./app/[user-id]` covers only the directory with exactly that name, in every list on every platform. Before v2.1.304, sandboxed commands on Linux and WSL2 failed to start while `denyRead` or `allowRead` held such an entry.
1403* **Braces and parentheses**: these are ordinary characters, so `~/data/{keys,tokens}` covers one directory named `{keys,tokens}`, not two alternatives. To cover `~/data/keys` and `~/data/tokens`, add each as its own entry.
1404 
13771405### `sandbox.filesystem.allowWrite`
13781406 
13791407Add paths where sandboxed commands can write, beyond the working directory, the per-user temp directory, and the directories you've added with `--add-dir`, `/add-dir`, or `permissions.additionalDirectories`. Use it when a subprocess such as `kubectl` or a build tool needs to write outside the project.
from line 3460
34323460 
34333461* **Scope**: [`Any file`](#scopes)
34343462* **Type**: Boolean
3435 * `true`: Claude Code appends the claude.ai session link when it commits or opens a pull request from a cloud or Remote Control session
3436 * `false`: Claude Code omits the link
3437* **Default**: `true`
3438 
3439```json settings.json theme={null}
3440{
3441 "attribution": {
3442 "sessionUrl": false
3443 }
3444}
3445```
3446 
3447## Hooks and automation
3448 
3449Register hooks, restrict which hooks run, and control workflows. For hook events and payloads, see the [hooks reference](https://code.claude.com/docs/en/hooks.md).
3450 
3451### `allowedHttpHookUrls`
3452 
3453Limit which URLs [HTTP hooks](https://code.claude.com/docs/en/hooks.md#http-hook-fields) can target. When you define this key, Claude Code runs an HTTP hook only if its URL matches one of the patterns and blocks the rest without running them; an empty array blocks every HTTP hook.
3454 
3455* **Scope**: [`Any file`](#scopes). Arrays merge across settings files.
3456* **Type**: array of URL patterns, with `*` as a wildcard
3457* **Default**: unset, so any URL is allowed
3458 
3459This example allows any URL under `https://hooks.example.com/` and any `http://localhost` URL:
3460 
3461```json settings.json theme={null}
3462{
3463 "allowedHttpHookUrls": ["https://hooks.example.com/*", "http://localhost:*"]
3464}
3465```
3466 
3467Hostname matching is case-insensitive and treats `hooks.example.com.`, with the trailing dot that marks a fully qualified domain name, the same as `hooks.example.com`, which is how DNS treats them. The allowlist applies to hooks from every source, including managed settings.
3468 
3469In the Agent SDK's `managedSettings` option, Claude Code ignores a list with entries, so pass it in the SDK's [`settings` option](https://code.claude.com/docs/en/agent-sdk/typescript#options) instead. See [what an empty list does in parent settings](https://code.claude.com/docs/en/claude-apps-gateway#how-hook-and-skill-keys-in-parent-settings-take-effect).
3470 
3471### `allowManagedHooksOnly`
3472 
3473Restrict hook execution to hooks your organization deploys.
3474 
3475* **Scope**: [`Managed`](#scopes)
3476* **Type**: Boolean
3477 * `true`: only managed hooks run, plus Agent SDK hooks and hooks from plugins your managed settings force-enable. See [What runs under `allowManagedHooksOnly`](#what-runs-under-allowmanagedhooksonly)
3478 * `false`: hooks from every settings scope and plugin run
3479* **Default**: unset, so hooks from every settings scope and pl
3463 * `true`: Claude Code appends the cla

artifacts Changed · +1 / -3 lines

from line 336
336336 
337337Claude Docs reaches Claude Code as a claude.ai [connector](https://code.claude.com/docs/en/mcp.md#use-mcp-servers-from-claude-ai) rather than a command. When it's connected, `/mcp` lists it as `claude.ai Claude Docs`. A request for a document meant for other people then goes to Claude Docs instead of an artifact page: a spec, a proposal, or a write-up of the plan you worked through in the session. Claude gives you the doc's link when it's drafted.
338338 
339A document that belongs in the codebase, such as a README, stays a file. To get a file for something Claude would otherwise put in Claude Docs, name the format, such as `.docx` or a Markdown file in the repository.
340 
341To turn the connector off, add `claude.ai Claude Docs` to `deniedMcpServers` or use the `/mcp` toggle, both described in [Disable claude.ai connectors](https://code.claude.com/docs/en/mcp.md#disable-claude-ai-connectors).
339A document that belongs in the codebase, such as a README, stays a file. To get a file for something Claude would otherwise put in Claude Docs, name the format, such as `.docx` or a Markdown file in the repository. To turn off only the `claude.ai Claude Docs` connector, see [Disable claude.ai connectors](https://code.claude.com/docs/en/mcp.md#disable-claude-ai-connectors).
342340 
343341## Page constraints
344342 

claude-code-on-the-web Changed · +1 / -1 lines

from line 4
44 
55> [!NOTE]
66>
7> Cloud sessions are available on Pro, Max, and Team plans, and for Enterprise users with premium seats or Chat + Claude Code seats. If your Enterprise organization manages your access with [custom roles](https://support.claude.com/en/articles/13930452), you also need a role that includes the cloud sessions permission. Ask an Owner to check your roles and add it at [**Organization settings > Roles**](https://claude.ai/admin-settings/roles) if it's missing.
7> Cloud sessions are available on Pro, Max, and Team plans, and for Enterprise users with premium seats or Chat + Claude Code seats. If [custom roles](https://support.claude.com/en/articles/13930452) govern your access, you also need a role with the cloud sessions permission to start sessions on Anthropic's infrastructure from the browser, apps, or terminal. Ask an Owner to add it to one of your roles at [**Organization settings > Roles**](https://claude.ai/admin-settings/roles).
88 
99A cloud session is a Claude Code session that runs on cloud infrastructure instead of on your machine. By default it runs on infrastructure Anthropic manages, or on your organization's [self-hosted environment](https://code.claude.com/docs/en/self-hosted-environments) when routed there. The session keeps running after you close your laptop, and you can check on it or steer it from any device. It counts toward your plan's usage limits alongside the rest of your Claude and Claude Code usage, and there's no separate charge for the cloud VM.
1010 

claude-tag Changed · +1 / -1 lines

This page is larger than the 256 KiB this site keeps, so one side of the diff below stops where the stored text does.

Nothing in the body moved in this read. What changed is above.

code-review-troubleshooting Changed · +2 / -1 lines

from line 63
6363 
6464## The check run mentions REVIEW\.md
6565 
66These are three of the messages a check run can carry about `REVIEW.md`:
66These are four of the messages a check run can carry about `REVIEW.md`:
6767 
6868* **"This review applied the repository's review instructions (REVIEW\.md) from its default branch, which is not protected against direct pushes."**: the review read the root file only. See [Which branch REVIEW.md is read from](https://code.claude.com/docs/en/code-review-customize.md#which-branch-review-md-is-read-from) for what protecting the branch changes.
6969* **"were not applied to this review: branch protection could not be confirmed"**: the review read no `REVIEW.md`.
from line 71
7171 * **Too many links**: it carries more than 64 Markdown links. Cut links to [64 or fewer](https://code.claude.com/docs/en/code-review-customize.md#include-other-files-in-review-md).
7272 * **A link the review can't parse**: it has a malformed Markdown link. Fix or remove the link.
7373 * **A link to a changed file**: it links to a file or folder this pull request changes, and the review couldn't leave out only the lines with that link. Write the rule in `REVIEW.md` itself.
74* **"include line(s) in the root REVIEW\.md were skipped on this review"**: your root `REVIEW.md` applied, but the files those lines name didn't. Check the lists under [Include other files in REVIEW.md](https://code.claude.com/docs/en/code-review-customize.md#include-other-files-in-review-md) and, for a skill, under [Include a skill file](https://code.claude.com/docs/en/code-review-customize.md#include-a-skill-file).
7475 
7576## Setup problems
7677 

mcp Changed · +1 / -1 lines

from line 1328
13281328 
13291329Servers from claude.ai appear in the list with indicators showing they come from claude.ai.
13301330 
1331Anthropic also provides some connectors itself, without you or an admin adding them. On accounts where [Claude Docs](https://code.claude.com/docs/en/artifacts.md#write-a-document-with-claude-docs) is available, `/mcp` lists `claude.ai Claude Docs` with no setup, and Claude uses it when you ask for a document meant for other people. To turn it off, add a `serverName` entry of `"claude.ai Claude Docs"` to `deniedMcpServers` or use the `/mcp` toggle, both described in [Disable claude.ai connectors](#disable-claude-ai-connectors).
1331Anthropic also provides some connectors itself, without you or an admin adding them. On accounts where [Claude Docs](https://code.claude.com/docs/en/artifacts.md#write-a-document-with-claude-docs) is available, `/mcp` lists `claude.ai Claude Docs` with no setup, and Claude uses it when you ask for a document meant for other people. To turn off only this connector, see [Disable claude.ai connectors](#disable-claude-ai-connectors).
13321332 
13331333Claude Code marks a connector `managed` in `/mcp` and in the [`/plugin`](https://code.claude.com/docs/en/plugins/install) manager when your organization manages its authentication in claude.ai. Managed status doesn't change how Claude Code connects to the connector or applies your organization's [tool controls](#organization-controls-on-connector-tools).
13341334 

self-hosted-environments-quickstart Changed · +2 / -0 lines

from line 117
117117* **The session fails to start with a git error**: if the error includes git's `could not read Username for` followed by your git host's URL, the runner had no HTTPS credentials for that host.
118118 * **Supply HTTPS credentials**: see [Configure git](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#configure-git)
119119 * **Clone over SSH instead**: if the runner host already has an SSH key, see [Rewrite git URLs for private networks](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#rewrite-git-urls-for-private-networks)
120 * **After either fix**: start the runner again and watch the failed session instead of starting a new one. If it has failed several times, wait several minutes for the runner to log `Picked up session`.
121 * **To use a different repository instead**: [archive](https://code.claude.com/docs/en/claude-code-on-the-web.md#archive-sessions) or [delete](https://code.claude.com/docs/en/claude-code-on-the-web.md#delete-sessions) the failed session, then start the runner again and start a new session. Otherwise, the runner can pick up the failed session instead of the new one.
120122 
121123### If the runner exits
122124 

self-hosted-environments-reference Changed · +2 / -2 lines

from line 79
7979 
8080| Flag | Default | Description |
8181| :- | :- | :- |
82| `--hook-concurrency <n>` | `4` | Maximum `spawn-runner` hooks running in parallel. Also caps how many spawn requests are claimed per poll. |
82| `--hook-concurrency <n>` | `4` | Maximum `spawn-runner` hooks running in parallel. Also caps how many spawn requests are claimed per poll. The orchestrator checks for new spawn requests only after every hook it started has finished, so one slow hook delays that orchestrator's next hooks. |
8383| `--hook-timeout <sec>` | `60` | Terminate the hook's process tree after this many seconds. The timeout plus its 5-second kill grace must stay below `--expected-spawn-seconds`; the orchestrator enforces this at startup. |
8484| `--expected-spawn-seconds <sec>` | `120` | Expected p99 time from when the orchestrator receives a spawn request to when the runner registers, including any wait for capacity on your platform. The server enforces a range of 10 to 3600. Sent on every poll as the server-side lease: if no runner registers before it elapses, the session is re-offered with a fresh order ID. All replicas must share this value. |
8585| `--min-idle <n>` | `0` | Keep at least N idle session slots free by spawning standby runners proactively. `0` disables pre-warming. Pair with the runner's `--exit-if-unused-min` so surplus standby runners reclaim themselves. |
from line 112
112112| `SELF_HOSTED_RUNNER_MAX_LIFETIME_GRACE_MS` | `900000` | How long the runner waits after a session reaches its `--kill-session-after-min` limit, for a running turn to finish or the release to complete, before it terminates the session |
113113| `SELF_HOSTED_RUNNER_POST_TURN_SETTLE_MS` | `7000` | Cap on how long the runner counts a session as busy for the `--drain-wait-sec` drain after a turn finishes, while the session's process reports the turn's end to Anthropic. `0` or an unusable value falls back to the default, so the hold can't be turned off. Requires Claude Code v2.1.275 or later. |
114114| `SELF_HOSTED_RUNNER_SIGKILL_GRACE_MS` | `30000` | How long the runner waits for the OS to deliver `SIGKILL` to a child stuck in uninterruptible I/O before exiting itself. Floored at `--post-session-hook-timeout-sec` plus 15 seconds, and 30 more when `--push-outcome-on-release` is set, so the effective minimum is 75 seconds at defaults. |
115| `CLAUDE_RUNNER_FETCH_DEPTH` | `50` | Git fetch depth for fresh clones. Set a positive integer, or `full` or `0` for a complete fetch. Repositories already present in the workspace keep their existing depth. |
115| `CLAUDE_RUNNER_FETCH_DEPTH` | `50` | Git fetch depth for fresh clones. Set a positive integer, or `full` or `0` for a complete fetch. For a clone that's already in the workspace, see [Reuse a pre-warmed checkout](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#reuse-a-pre-warmed-checkout). |
116116| `CLAUDE_RUNNER_FETCH_SERVER_PROGRESS_CAP_MS` | `600000` | How long in milliseconds, per attempt, a git fetch may wait for its first data while the git server's own progress numbers keep rising, as when the server prepares the pack for a large repository. `0` or `off` turns the wait off: such a fetch is then cut off after two minutes without data. Any other whole number is clamped to between `120000` and `1800000`, 2 to 30 minutes. Requires Claude Code v2.1.295 or later. |
117117| `CLAUDE_RUNNER_SKIP_GIT_VERIFY` | unset | When `1`, skip the `.git` presence check after a `checkout` hook runs. Set this when your hook materializes a non-git source. |
118118| `FORCE_AUTOUPDATE_PLUGINS` | unset | When `1`, let plugin marketplaces auto-update even though the binary is pinned |
Feedback