anthropics / anthropics/claude-code

[BUG] Claude Code on the Web: a repository's .claude/settings.json is silently ignored, so its hooks never run

Ouverte
#89,215 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub
area:claude-code-web area:hooks bug has repro platform:web
Langage dominant
Python
Étoiles
145k
Forks
23.1k
Métriques de merge des PR
Métriques de PR en attente

Description

### What's Wrong?

In a Claude Code on the Web cloud session, the session's project root is the **parent** directory of the repository checkout, not the checkout itself. Claude Code therefore never loads `/.claude/settings.json`, and any hooks registered there never fire. Nothing warns — not the session, not the logs.

This contradicts the documented contract in [Configure cloud environments](https://code.claude.com/docs/en/cloud-environments), which:

- lists "Your repo's `.claude/settings.json` hooks" as carrying over to a cloud session — *"Yes — part of the clone"*
- tells users to *"add a SessionStart hook to your repo's `.claude/settings.json`"* for dependency installation
- states that *"`$CLAUDE_PROJECT_DIR` resolves to the repository root, so the hook finds the script regardless of the session's working directory"* — while in our sessions `$CLAUDE_PROJECT_DIR` was **empty**

**Likely trigger (hypothesis, untested):** our environment attaches **two** repositories. The documentation describes the single-repository case, where the project root is the checkout. With two attached repositories the project root appears to become their common parent, `/home/user`. A single-repository session may well behave as documented; we did not test one.

**Impact.** Repositories that use a `SessionStart` hook to finish environment setup (install a toolchain, hydrate Git LFS, initialize submodules) start every cloud session with that work undone, then fail at the first build or test — minutes into the conversation, far from the cause.

**Why it is hard to diagnose from inside a session:**

- A silently ignored settings file looks identical to a hook that ran and did nothing.
- The launcher's own `SessionStart` hook (`session-start-git-identity.sh`, from `/root/.claude/launcher-settings.json`) supplies a decoy signal in the diagnostic log with a plausible exit code and duration, so the failure reads as "the repo's hook ran and exited cleanly."
- Hook stdout never reaches the session's context ([#10373](https://github.com/anthropics/claude-code/issues/10373)), so a hook made deliberately loud is still invisible to the agent.
- `CLAUDE.md` **is** loaded from the checkout, including its `@`-imports — so file discovery reaches the subdirectory while settings loading does not, which actively suggests the repo is being treated as the project.

It took four cloud test-session rounds to locate this; two intermediate rounds produced fixes for a misdiagnosis, because every in-session signal pointed at the hook's own logic rather than at registration.

### What Should Happen?

Either `.claude/settings.json` is loaded from each attached repository's checkout, or Claude Code says it is skipping it. A silent skip of a version-controlled configuration file is the core problem; the hook is only the most visible casualty.

Suggested fixes, in order of preference:

1. Load `.claude/settings.json` from each attached repository's checkout.
2. If that is not intended, emit a warning naming the path that was skipped and the project root that was used.
3. Document the project-root behaviour for multi-repository cloud sessions, and provide a supported way to register a `SessionStart` hook that belongs to a repository.

### Error Messages/Logs

There is no error. That is the defect. The only `SessionStart` evidence in the diagnostic log belongs to the launcher's own hook:

```json
{"timestamp":"2026-08-24T11:42:09.499Z","level":"info","event":"hook_spawn_started","data":{"hook_event_name":"SessionStart","index":0}}
{"timestamp":"2026-08-24T11:42:09.508Z","level":"info","event":"hook_spawn_completed","data":{"hook_event_name":"SessionStart","index":0,"duration_ms":15,"exit_code":0,"aborted":false}}
```

Environment observations from the session:

```
$ ls /root/.claude/projects/ # exactly one project: the parent, not the checkout
-home-user

$ ls /home/user/.claude/ # does not exist
ls: cannot access '/home/user/.claude/': No such file or directory

$ echo "[${CLAUDE_PROJECT_DIR:-}]" # empty in the hook process
[]
$ echo "$CLAUDE_CODE_REMOTE"
true
$ echo "$PWD" ; echo "$HOME"
/home/user/
/root
```

The repo's own hook never appears in any log, and its script never executes (verified by it writing a file on every branch, including its early-exit branch — the file is never created).

### Steps to Reproduce

1. Create a cloud environment with **two attached repositories**.
2. In one repository, commit `.claude/settings.json` registering a `SessionStart` hook that proves it ran, e.g.:

```json
{
"hooks": {
"SessionStart": [
{ "hooks": [ { "type": "command", "command": "date > /tmp/hook-ran.log" } ] }
]
}
}
```

3. Start a new cloud session on that repository.
4. Check `/tmp/hook-ran.log` — it does not exist. Check `ls /root/.claude/projects/`, which shows a single project entry for `/home/user`, the parent of the checkout.

The hook is never registered because `/home/user//.claude/settings.json` is one level below the project root. A hook with an explicit `"matcher": "startup|resume"` behaves the same; we later registered the identical hook entry into `~/.claude/settings.json` and `/home/user/.claude/settings.json` from the environment's setup script, and it then fired on every session — so the hook definition itself was always valid.

### Related issues

- [#33815](https://github.com/anthropics/claude-code/issues/33815) (closed): `$CLAUDE_PROJECT_DIR` empty/unset in hook execution. We observe the same emptiness in cloud sessions, which is why the workaround below cannot rely on it.
- [#10373](https://github.com/anthropics/claude-code/issues/10373) (open): `SessionStart` hook stdout never processed for brand-new conversations. Compounds this bug — a hook made deliberately loud is still invisible to the session. We have added a cloud-session confirmation there rather than duplicating it here.
- [#19134](https://github.com/anthropics/claude-code/issues/19134) (closed as stale): documents the same repo-level `SessionStart` hook pattern for cloud dependency management — the pattern this bug silently breaks.

### Workaround, for anyone else hitting this

Have the environment's setup script (which runs at snapshot-build time, as root) merge the hook registration into the settings a session actually reads — `$HOME/.claude/settings.json` and `/.claude/settings.json` — preserving any existing content so the launcher's own hooks survive. Confirmed working: the hook then fires on every session.

### Claude Code Version

Unknown — Claude Code on the Web cloud session, observed 2026-08-24. The cloud environment provisions its own Claude Code build; no version string is surfaced in the session.

Guide de contribution

Aucun guide de contribution indexé pour ce dépôt

Piste de recherche

Start by reproducing the two-attached-repository setup and inspect how the cloud session chooses its project root and loads .claude/settings.json. Compare the repository hook with the working registration in ~/.claude/settings.json, and check the project and hook diagnostics described in the issue. Done means repository settings are loaded, or a clear warning and supported registration path explains when they are skipped.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
json
Domaine
cloud, devtools
Type d'issue
Bug
Difficulté
4/5
Temps estimé
3-5 jours
Activité
Active
Clarté
Plutôt claire
Accessibilité débutants
42/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.