[BUG] claude-api skill inlines its entire ~800KB doc set when language detection fails, even on deliberate invocation

Status Fixed / completed
Reported on v2.1.221
Maintainer reply None cached
Activity 2 comments · opened Aug 4, 2026 · closed Aug 17, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

This is a different failure mode from the mis-trigger reports (#74473, #79457, #79463, #81059, #83557, #74621, #69179): here the invocation is deliberate and correct — a custom slash command delegates to /claude-api prompt-audit <file.md>. The skill should load; it owns the audit methodology. The waste happens after that decision, in doc selection. Fixing the trigger heuristics therefore won't fix this case.

All eight language doc sets are injected for a language-agnostic subcommand. The single tool result contained doc blocks for csharp, curl, go, java, php, python, ruby and typescript, plus the 26 shared/*.md files. The skill's own text says (emphasis mine):

for the prompt-audit subcommand, skip this section's ask steps — the audit is non-interactive and its inventory is language-agnostic

The subcommand's doc needs are language-independent by the skill's own account; all eight language-specific sets are injected regardless.

The same failed-detection path also emits dead references. The assembled body contains:

Chat UI or real-time response display: → Refer to unknown/claude-api/README.md + unknown/claude-api/streaming.md

No unknown/ directory exists in the bundle. Whatever the assembly mechanism, an unresolved detection result is being interpolated into paths verbatim and the model is told to consult files that do not exist. I read this as the same defect surfacing twice rather than a separate bug: the failed-detection branch is unhandled, so it both falls through to "inline everything" and leaves its sentinel in the rendered paths.

What Should Happen?

Language detection returning no result should be handled as a branch, not carried forward as a value.

Concretely, for /claude-api prompt-audit on a markdown target: inject shared/prompt-audit.md, and no language-specific doc sets. More generally, when detection fails the assembled body should reference paths that exist — either real per-language paths offered for on-demand Read, or none at all — never a literal unknown/ prefix.

Steps to Reproduce

  1. Create a directory containing no SDK source files and a single markdown target:

``bash
mkdir -p /tmp/repro && cd /tmp/repro
printf -- '---\ndescription: test command\n---\n\nRewrite the user input clearly.\n' > target.md
``

  1. Start Claude Code in /tmp/repro. Language detection has nothing to find, which is correct for this directory.
  2. Invoke the subcommand directly:

``
/claude-api prompt-audit /tmp/repro/target.md
``

  1. Inspect the resulting tool call. Observe in the single skill tool result:
  • <doc path="..."> blocks for all eight languages (csharp, curl, go, java, php, python, ruby, typescript) plus the shared/*.md set;
  • under Quick Task Reference, references to unknown/claude-api/README.md and unknown/claude-api/streaming.md.
  1. Run /context to see the resulting context consumption.

Expected: shared/prompt-audit.md and nothing language-specific. Actual: the full bundle inlined.

Is this a regression?

I don't know

Claude Code Version

2.1.221 (Claude Code)

Platform

Anthropic API

Operating System

Linux (Arch)

Terminal/Shell

Ghostty / bash

Additional Information

Measured on the on-disk bundle (2.1.221):

| | Bytes |
|---|---|
| shared/prompt-audit.md — the only file the subcommand needs injected up front | 32,954 |
| Full skill directory | 812,650 |
| Useful / loaded (by bytes) | ~4% |

The methodology cross-references two other shared files (model-migration.md, prompt-caching.md); those are read-on-demand candidates, not up-front injections, and all three together are still well under 10% of the bundle.

That is roughly 200k tokens if the full directory is inlined (bytes/4 estimate; the observed tool result did contain all eight language sets plus shared/). Caching doesn't neutralise it: short maintenance sessions each pay a fresh cache write, and even when a session lasts long enough to get cache reads, ~200k tokens of context-window space is gone regardless of price. For recurring prompt-maintenance workflows — auditing skills, commands, CLAUDE.md and rule files across many repositories — this dominates every session.

To be clear: shipping 812KB in the bundle is fine — inlining it per invocation is the bug.

Prior art on age: this is not new to 2.1.221. #74473 reports the same inlining on 2.1.201 and 2.1.205, and #83557 on 2.1.220. I have not tested far enough back to say whether a version ever behaved differently, hence "I don't know" above.

Possible fixes, in rough priority:

  1. Select docs by subcommand first: prompt-audit needs shared/prompt-audit.md injected; its two cross-referenced shared files can be read on demand.
  2. Treat failed language detection as a branch, not a value: never interpolate the sentinel into doc paths, and instead of inlining all eight sets, inject none and list the per-language paths for on-demand Read — the files ship in the bundle either way.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗