[BUG] Skill listing is permanently lost when the first user turn is interrupted and resubmitted (retraction sibling of #82017)

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 0 comments · opened Aug 2, 2026

Summary

The skill_listing attachment is sent once per session, anchored to a single user
message. If that message is retracted — the user presses Esc during the first turn and then
resubmits or lightly edits the same prompt — the attachment is discarded together with the
message, and it is never re-sent for the rest of the session. The model is then routing-
blind to every installed skill.

This is the same root cause as #82017 (compaction-continued sessions lose the inventory),
with a different trigger. Both are instances of one defect: the "already delivered" state is
not reconciled when the messages carrying the listing leave the context. #82017 covers the
compaction path; this issue covers the retraction path, which needs no long session and can
happen within the first ten seconds of a fresh one.

Environment

  • Claude Code 2.1.220, native install
  • macOS 26.6 (arm64)
  • 33–36 skills registered (user-level ~/.claude/skills/ + project-level .claude/skills/)

Reproduction

  1. Start a session in a project with skills available.
  2. Send the first prompt of the session.
  3. While the assistant is responding, press Esc to interrupt.
  4. Resubmit the same prompt (verbatim or lightly edited).
  5. Ask the model to list its available skills, or give it a task an installed skill covers.

Observed: the model reports that no skill listing is present, and routes to generic tools.

Evidence from session JSONL

Across 668 local sessions that received a skill_listing attachment, 13 (~2%) match the
pattern "listing anchored to the first prompt → Esc interrupt → same prompt resubmitted".

Two show the downstream effect unambiguously:

  • Session Askill_listing at line 7 (skillCount=29, isInitial=true), interrupt at

line 16, prompt resubmitted at line 18. Afterwards the model enumerated what it had
received as "CLAUDE.md, rules, project memory, agent list, deferred tools" — skills absent
— and told the user a capability did not exist, while a skill providing it was installed.

  • Session Bskill_listing at line 13 (skillCount=33, isInitial=true), interrupt at

line 40, prompt resubmitted at line 42. The model then hand-rolled the task with curl
instead of the skill that covers it, and reported no skill listing when asked.

In both, the post-retraction user turns carry deferred_tools_delta, agent_listing_delta
and mcp_instructions_delta attachments but no skill_listing — the other inventories are
re-emitted, the skill inventory is not.

Control: interrupts occurring later in a session, after the anchor turn completed, do
not lose the listing — verified in sessions where the model correctly enumerated all skills
following an interrupt.

Likely root cause

The attachment builder for skill_listing computes a delta against per-session state and
returns nothing when there are no new skills; the renderer then drops the block entirely
because its content is empty. That state is written when the listing is first emitted and is
restored on resume by restoreSkillStateFromMessages, but it is not reconciled when messages
are removed by dropRetractedMessages / removeInterruptedMessage. The state says
"delivered" while the conversation no longer contains the delivery.

Suggested fix

When retracting messages, remove any skill_listing attachment they carry from the recorded
state so the next turn re-emits an initial listing. More generally: reconcile the state
against the surviving messages before computing the delta — the same reconciliation already
performed on resume — which would close the compaction path in #82017 at the same time.

Workaround

/reload-skills re-injects the full listing (reported for the compaction path in #82017; the
command is present in 2.1.220). Otherwise: do not interrupt-and-resubmit the very first turn,
or start a new session if you did.

Impact

Silent: nothing errors, skills simply stop being considered. It is also hard to diagnose from
the user side, because the model's self-reports about whether the listing is present are
unreliable in both directions — in sessions where the listing was delivered and demonstrably
used, the same model still claimed it was missing when asked to justify a skill it failed to
route to.

View original on GitHub ↗