npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

opencode-thinking-signature-guard

v0.1.1

Published

OpenCode plugin that removes non-replayable Anthropic thinking blocks (redaction-poisoned / short-signature turns) so sessions stop failing with 400 'Invalid signature in thinking block'.

Downloads

311

Readme

opencode-thinking-signature-guard: stop Anthropic "Invalid signature in thinking block" 400s in OpenCode

An OpenCode session using Anthropic (Claude) with extended thinking can get permanently stuck on:

AI_APICallError: messages.N.content.M: Invalid `signature` in `thinking` block

Once it starts, every retry fails the same way and the session cannot continue. opencode-thinking-signature-guard is a small, zero-config OpenCode plugin that removes the offending thinking blocks from the outgoing request so the session recovers. It does not edit your saved transcript; it only rewrites the message array OpenCode is about to send to the model.

The problem

Anthropic re-validates the cryptographic signature on every thinking block you replay in the conversation history. Most signed thinking blocks replay fine. The ones that do not are the turns where Anthropic engaged safety redaction: those turns come back with redacted_thinking blocks plus a short "lightweight" signature (~236 characters, metadata only, no encrypted payload). That short signature is not replayable.

The moment one such turn lands in your history, it poisons the whole session: every subsequent request replays it and Anthropic rejects the entire request with 400 Invalid signature in thinking block. This is not specific to any proxy, prompt-cache setup, or organization, and it is unrelated to session size — a single poisoned turn is enough. The same failure is reported across many Anthropic-based tools (see References).

What the plugin does

It hooks experimental.chat.messages.transform, which runs right before OpenCode serializes messages for the model, and:

  1. Finds poisoned assistant turns — a turn that contains a redacted_thinking reasoning part, or a signed thinking part whose signature is shorter than a threshold (default 256).
  2. Removes all reasoning parts from those turns, leaving their text and tool calls intact. Anthropic accepts a completed tool-use turn that has no thinking block, so the request validates again.
  3. Leaves healthy turns (normal long signatures, no redaction) completely untouched, so their prompt cache and reasoning continuity are preserved.

This targeted approach is why the default does not simply strip all history thinking: healthy turns keep their signatures and stay cacheable, and only the turns that are already causing a hard 400 are modified.

Verified against the real failure

On the exact request that produced a real 400 in production (131 messages, one poisoned turn: redacted_thinking × 3 + thinking(sig len 236) + text + tool_use × 2):

| Request | Result | | --- | --- | | Original, unmodified | 400 messages.*.content.*: Invalid signature in thinking block | | After this plugin's policy | 200 OK (stream starts normally) |

Installation

OpenCode auto-installs the plugin when its npm name appears in opencode.json (see Quick start). To install it manually:

bun add opencode-thinking-signature-guard
# or
npm install opencode-thinking-signature-guard

Requires Node.js >=18.17 and OpenCode >=1.15.12.

Quick start

Add the package name to opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["opencode-thinking-signature-guard"]
}

That is all. With defaults, the plugin only acts on poisoned turns and is otherwise invisible.

To use a local checkout instead of npm, point at the built file:

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["file:///abs/path/to/opencode-thinking-signature-guard/dist/plugin.js"]
}

Configuration

Options are optional. Pass them with the tuple form in opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": [
    [
      "opencode-thinking-signature-guard",
      {
        "mode": "redacted",
        "shortSignatureThreshold": 256,
        "dryRun": false,
        "verbose": false,
        "log": false
      }
    ]
  ]
}

| Option | Default | What it means | | --- | --- | --- | | mode | "redacted" | "redacted": strip reasoning only from poisoned turns (redacted or short-signature). "all-stale": also strip reasoning from every completed assistant turn before the active user boundary (more aggressive; sacrifices prompt cache but maximally defensive). "off": disable. | | shortSignatureThreshold | 256 | A signed thinking block whose signature is shorter than this is treated as a non-replayable redaction signature. The real poisoned signatures are ~236 chars; healthy ones are 500+. | | dryRun | false | Detect and log without mutating the outgoing request. | | verbose | false | Log a one-line summary to stderr whenever a request is modified. | | log | false | Append JSON-line records to ~/.local/share/opencode/thinking-signature-guard.log. |

Every option also has an environment-variable equivalent, which takes effect even without editing opencode.json:

THINKING_SIGNATURE_GUARD_MODE=redacted        # redacted | all-stale | off
THINKING_SIGNATURE_GUARD_SHORT_SIG=256
THINKING_SIGNATURE_GUARD_DRYRUN=1
THINKING_SIGNATURE_GUARD_VERBOSE=1
THINKING_SIGNATURE_GUARD_LOG=1

When to use it

Reach for this plugin when an OpenCode session on Claude with extended thinking starts failing every turn with Invalid signature in thinking block, especially after a long or tool-heavy run. If you never see that error, the plugin stays dormant and changes nothing.

Caveats

  • Prompt cache: modifying a turn changes the request prefix from that point on, which invalidates Anthropic's prompt cache after the poisoned turn. This is unavoidable — the poisoned turn already forces a 400, so there is no working cache to preserve. Healthy turns are left intact precisely to keep as much cache as possible.
  • Scope: the plugin targets the Anthropic redaction/replay case (metadata.anthropic.signature / metadata.anthropic.redactedData). It does not touch Bedrock/Vertex thought signatures or non-Anthropic providers.
  • Fail-open: if anything unexpected happens inside the hook, the plugin passes the request through unmodified rather than breaking your session.

Development

bun install
bun run typecheck   # tsgo --noEmit
bun run check       # biome
bun run test        # fixture + hook tests
bun run build       # emit dist/

References

License

MIT © isac322