`claude plugin list --json` silently truncates at 64KB when output is piped

Resolved 💬 3 comments Opened Mar 20, 2026 by technicalpickles Closed May 24, 2026

Description

When claude plugin list --json output is captured via a subprocess pipe, the output is silently truncated at exactly 65,536 bytes (64KB). Redirecting to a file produces the full output. This breaks any programmatic consumer that parses the JSON.

Reproduction

import subprocess, json

# Pipe capture: truncated at 64KB
result = subprocess.run(
    ["claude", "plugin", "list", "--json"],
    capture_output=True, text=True
)
print(len(result.stdout))  # 65536
json.loads(result.stdout)  # JSONDecodeError at char 65536
# File redirect: full output
claude plugin list --json > /tmp/out.json
wc -c /tmp/out.json  # 93533 (valid JSON)
# Pipe to wc: also truncated
claude plugin list --json | wc -c  # 65537 (65536 + newline)

Environment

  • Claude Code 1.0.41
  • macOS 15.4 (Darwin 25.3.0, arm64)
  • 235 plugins installed across multiple projects (~93KB JSON output)

Impact

Any tool that shells out to claude plugin list --json and parses the result via pipe gets silently broken once the user has enough plugins installed. The JSON is cut off mid-object, producing a parse error. There is no error message or non-zero exit code to indicate truncation occurred.

In our case, this broke tool-routing plugin's route discovery, which calls claude plugin list --json via subprocess.run to find other plugins' route files. Discovery silently returned zero routes, so hooks never blocked anything.

Likely cause

This appears to be a systemic 64KB buffer pattern in the codebase. Issues #29088 and #35085 document that 65536 (R1 = 65536, Buffer.allocUnsafe(R1)) is used as a buffer size for reading file/stream content in multiple places. The same limit seems to apply to CLI JSON output when piped.

For stdout specifically: Node.js process.stdout has a default highWaterMark of 64KB for writable streams. If process.exit() is called before the write buffer fully drains to a pipe, the remaining data is lost. File descriptors opened by shell redirection (>) bypass this because the kernel handles the fd directly.

Workaround

Write subprocess stdout to a temp file instead of using pipe capture:

import tempfile, subprocess, json
from pathlib import Path

with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as tmp:
    tmp_path = tmp.name

with open(tmp_path, "w") as f:
    subprocess.run(["claude", "plugin", "list", "--json"], stdout=f)

data = json.loads(Path(tmp_path).read_text())  # works

Related issues

  • #31408 - Exact same bug (closed/locked by bot). "Plugin browser shows 0 plugins due to truncated JSON from CLI." Documents the JSON getting cut off mid-object, same as here.
  • #29088 - 64KB head buffer limit truncates large session entries (same 65536 constant)
  • #35085 - fetchSessions() silently drops large sessions due to 64KB head/tail reader
  • #27247 - enabledPlugins scope bug (different root cause, same impact area)

View original on GitHub ↗

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