Plugin install/update writes ~/.claude/plugins/npm-cache/package.json without truncating, leaving trailing bytes from previous content

Resolved 💬 2 comments Opened May 14, 2026 by deeepanshu Closed Jun 12, 2026

Summary

\~/.claude/plugins/npm-cache/package.json\ ends up with valid JSON followed by leftover bytes from a previous write. Subsequent \claude plugin update\/\install\ then fails with \EJSONPARSE\ and the user is locked out of plugin updates until the file is manually repaired.

Error

\\\
✘ Failed to update plugin "<plugin>@<marketplace>": Failed to install npm package: npm error code EJSONPARSE
npm error JSON.parse Invalid package.json: JSONParseError: Unexpected non-whitespace character after JSON at position 114 (line 7 column 3) while parsing near "...: \"^X.Y.Z\"\n }\n}\n }\n}\n"
\
\\

Evidence

\xxd ~/.claude/plugins/npm-cache/package.json\ on the affected machine:

\\\
00000000: 7b0a 2020 2264 6570 656e 6465 6e63 6965 {. "dependencie
00000010: 7322 3a20 7b0a 2020 2020 2240 7363 6f70 s": {. "@scop
00000020: 652f 706c 7567 696e 2d61 223a 2022 5e58 e/plugin-a": "^X
00000030: 2e59 2e5a 222c 0a20 2020 2022 4073 636f .Y.Z",. "@sco
00000040: 7065 2f70 6c75 6769 6e2d 6222 3a20 225e pe/plugin-b": "^
00000050: 582e 592e 5a22 0a20 207d 0a7d 0a20 207d X.Y.Z". }.}. }
00000060: 0a7d 0a .}.
\
\\

Trailing bytes \ }\n}\n\ after the closing \}\n\ are leftovers from a previous, longer JSON document. The current intended JSON is shorter than the file on disk.

Root cause analysis

  • \~/.claude/plugins/npm-cache/package-lock.json\ lists a different dependency set than \package.json\ does, indicating a recent plugin operation changed the dependency set.
  • Old content size > new content size.
  • Writer overwrote starting at offset 0 but did not truncate, leaving the suffix of the previous content intact past the end of the new content.

Consistent with writing via \fs.writeFile\/\writeFileSync\ without \flag: 'w'\ semantics, or opening with \O_WRONLY | O_CREAT\ without \O_TRUNC\, or using \'r+'\/\'a'\ flags.

Repro hypothesis

  1. Install plugins A + B (writes a longer package.json).
  2. Switch to plugins B + C, where the new package.json is shorter in bytes.
  3. On-disk file now contains the new JSON followed by the tail of the old JSON.
  4. Next \claude plugin install/update/list\ reads the file via npm, fails with EJSONPARSE.

Workaround

Manually rewrite the file with valid JSON:

\\\bash
cat > ~/.claude/plugins/npm-cache/package.json <<'JSONEOF'
{
"dependencies": {
"@scope/plugin-b": "^X.Y.Z",
"@scope/plugin-c": "^X.Y.Z"
}
}
JSONEOF
\
\\

Plugin operations recover after this.

Suggested fix

When writing \~/.claude/plugins/npm-cache/package.json\, ensure the write truncates the file (e.g., \fs.writeFileSync(path, content, { flag: 'w' })\, or write to a tempfile and atomically \rename\ over the target).

Environment

  • Claude Code: 2.1.140
  • Node: v22.13.1
  • OS: macOS (Darwin 25.4.0)

View original on GitHub ↗

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