anthropics / anthropics/claude-code

[BUG] StopFailure fires for internal helper queries and subagent failures on the parent session; under low-priority mode every excluded helper 429 reports a failed turn that never happened

オープン
#91,419 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る
area:agents area:hooks bug has repro platform:windows
主要言語
Python
スター
145k
フォーク
23.1k
PR マージ指標
PR 指標を取得中

説明

## Summary

`StopFailure` fires for API errors in Claude Code's own internal helper queries (prompt suggestion, away summary, memory extraction, agent summary) and for background subagent failures, always with the parent `session_id`. Under low-priority mode those helpers are excluded from retry, so while a usage limit is exhausted each one dies on a 429 and fires `StopFailure` at an idle prompt. Any hook consumer that reads the event as "the turn ended on an API error" (its documented meaning) shows a failed turn that never happened.

## Environment

- Claude Code 2.1.258, native binary, Windows 11
- Interactive REPL, `bypassPermissions`, `/low-priority` enabled after hitting the 5-hour limit
- `StopFailure` hook wired to a small command hook that forwards the payload to a desktop notifier

## What happened

1. Main loop launched nine background agents and parked ("waiting on 9 agents", `Stop` with a non-empty `background_tasks`). Seven agents died on `429 rate_limit`; each death fired `StopFailure` on the parent session, and each failed task-notification woke the main loop, which hit the same 429 and fired `StopFailure` again. Fourteen `StopFailure` hooks in about a minute for one underlying limit.
2. Enabled `/low-priority`. The main loop and agents resumed correctly.
3. Two minutes later, with the main loop still parked and no user input, three more `StopFailure` hooks fired (`error: "rate_limit"`, no `error_details`) about 20-30 s apart. No transcript line, no subagent transcript line, and no task output records any API error in that window. The next entry in the session transcript is a completed-agent task-notification six minutes later.

## Why (from the 2.1.258 bundle)

- The forked helper queries all go through the same query generator as the main turn: the `Gv` fork helper (`querySource` in `prompt_suggestion`, `away_summary`, `extract_memories`, `agent_summary`, `skipTranscript: true`) iterates `FO`, which runs the main loop generator (`o1n`, both branches).
- That generator's API-error exit calls the `StopFailure` emitter (`fOe`) for any `isApiErrorMessage`, using `session.id`. The only skip is a delegated-observation subagent context. Nothing distinguishes a fork, a subagent, or the main turn in the payload; the schema is base fields plus `error`, `error_details`, `last_assistant_message`.
- The low-priority retry allow-list (`JZ`: `agent:*`, `compact`, `hook_agent`, `hook_prompt`, `side_question`, `web_search_tool`, `web_fetch_apply`, `repl_sampling`, `auto_mode*`, `chrome_mcp`, `artifact_comment_reply`, plus the builtin agent keys) excludes those helpers. For an excluded source the 429 handler returns null, so the request errors instead of waiting.
- Result: with the limit exhausted, every helper fork fails immediately and fires `StopFailure`, while the real turn is fine.

Which helper fired in step 3 is inferred (the payload carries nothing to tell, and forks skip the transcript); the timing matches returning to an idle prompt.

## Expected

`StopFailure` means the turn ended on an API error. Helper forks and background subagent deaths are not the turn ending. The docs describe it that way and consumers (auto-continue hooks, dashboards, notifiers) depend on it.

## Suggested fix

Either of these would do; both would be best:

- Do not fire `StopFailure` from `skipTranscript` forks, or more generally from non-main `querySource`s. A subagent death already surfaces through its failed task-notification and the re-woken main loop's own outcome.
- Add `query_source` (and `agent_id` when present) to the `StopFailure` payload so consumers can filter, the way `SubagentStop` already carries `agent_id`.

Separately, the pre-low-priority cascade in step 1 is worth a look: each dead agent re-wakes a main loop that cannot succeed, so one exhausted limit produces `2N` `StopFailure` hooks.

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

調査の方向性

Start by locating the StopFailure emitter (fOe), fork helper (Gv), main loop generator (o1n), and low-priority retry allow-list (JZ) in the 2.1.258 bundle. Reproduce the idle-prompt scenario with /low-priority, background agents, and a StopFailure command hook. Done means helper forks and background subagent failures no longer create misleading parent StopFailure events, with query_source and agent_id included if that option is chosen.

索引モデルが issue の本文から書いたものです。

評価

技術スタック
python
領域
api, cli
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
活発
明瞭さ
おおむね明確
初心者へのやさしさ
45/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。