[BUG] Headless claude -p poisons global mcp-needs-auth-cache.json for the telegram channels plugin — subsequent --channels sessions silently never spawn their poller

Status Open
Reported on v2.1.218
Maintainer reply None cached
Activity 1 comment · opened Jul 24, 2026

Summary

One unrelated headless claude -p invocation on a machine silently disables all Telegram channel bots started in the following minutes, via the global ~/.claude/mcp-needs-auth-cache.json.

Mechanism:

  1. The telegram channels plugin (plugin:telegram@claude-plugins-official) is installed user-wide. Real bots each run with their own TELEGRAM_STATE_DIR (multi-bot isolation); the plugin's default state dir (~/.claude/channels/telegram) has no active bot token configured.
  2. A headless claude -p "ok" is run without TELEGRAM_STATE_DIR set. The plugin resolves its default state dir, finds no token, and Claude Code writes a plugin:telegram:telegram entry into the global ~/.claude/mcp-needs-auth-cache.json.
  3. Any claude --channels plugin:telegram@claude-plugins-official session started while that cache entry is fresh — even with TELEGRAM_STATE_DIR pointing at a fully-configured state dir with a valid token — silently never spawns its Telegram poller (the bun child). The session runs, registers .in_use, but never polls. No error or warning is surfaced anywhere.
  4. The cache entry ages out after some minutes, after which restarts behave normally — which makes the failure look random/transient and very hard to attribute.

The cache is keyed by plugin/server name only, so a needs-auth verdict computed against one state dir (the unconfigured default) is applied to sessions using a different, fully-authorized state dir.

Environment

  • Claude Code: 2.1.218
  • Telegram plugin: 0.0.6 (telegram@claude-plugins-official), installed user-wide
  • OS: Windows 11 Pro (10.0.26200)
  • Setup: 3 channel bots, each launched with its own TELEGRAM_STATE_DIR (own .env token + access.json); default state dir token intentionally commented out; a scheduled credential-refresh job runs claude -p "ok" periodically (this was the poisoning trigger)

Steps to reproduce

  1. Install the telegram plugin user-wide. Leave the default state dir (~/.claude/channels/telegram) without a valid bot token.
  2. Prepare a working state dir, e.g. ~/.claude/channels/telegram-mybot, with a valid .env token + access.json.
  3. Run claude -p "ok" with no TELEGRAM_STATE_DIR in the environment.
  4. Observe ~/.claude/mcp-needs-auth-cache.json now contains a fresh plugin:telegram:telegram entry.
  5. Within the cache TTL, launch TELEGRAM_STATE_DIR=~/.claude/channels/telegram-mybot claude --channels plugin:telegram@claude-plugins-official.
  6. The session starts and registers .in_use, but no poller process is spawned (no bun child, no bot.pid written). Messages sent to the bot are never pulled. No error is shown.
  7. Delete the cache entry (or wait for it to expire) and restart the channel session → poller spawns immediately and polling resumes.

Observed evidence (2026-07-24, ~11:07 local)

Supervisor watchdog log (checks each bot's claude session + its poller pid):

[2026-07-24 11:06:01] inbox:    claude=pid 31284 age 5.9m | bot.pid=31460 wrote 11:00:07 -> ALIVE
[2026-07-24 11:06:01] missions: claude=pid 29172 age 5.9m | bot.pid=23332 wrote 11:00:07 -> ALIVE
[2026-07-24 11:06:01] kztrip:   claude=pid 2932  age 5.9m | bot.pid=3480  wrote 11:00:07 -> ALIVE
[2026-07-24 11:09:01] inbox:    claude=pid 18932 age 1.5m | bot.pid=31460 wrote 11:00:07 -> DEAD
[2026-07-24 11:09:01] missions: claude=pid 17524 age 1.5m | bot.pid=23332 wrote 11:00:07 -> DEAD
[2026-07-24 11:09:01] kztrip:   claude=pid 28112 age 1.5m | bot.pid=3480  wrote 11:00:07 -> DEAD

All 3 bots were restarted at ~11:07:30 (right after the scheduled claude -p "ok"), and all 3 fresh sessions came up without a poller. The plugin:telegram:telegram timestamp in mcp-needs-auth-cache.json matched the claude -p run to the second. Removing the entry and recycling the channel sessions restored polling on all 3 bots immediately.

This root cause was only found after two earlier wrong theories, precisely because (a) nothing is logged/surfaced when the poller is skipped, and (b) the cache entry expires within minutes, so later manual restarts always worked.

Expected behavior

  • A needs-auth verdict for a channels plugin should be scoped to the configuration it was computed against (the resolved state dir), not applied globally by plugin name to sessions using a different, fully-configured state dir.
  • If a --channels session skips spawning its poller for any reason (including a needs-auth cache hit), that should be surfaced visibly in the session — a silent no-op bot is the worst failure mode for an unattended channel.

Suggested fixes

  1. Scope the needs-auth cache per state dir / per resolved config (e.g. key on plugin name + state dir hash), so a headless run against the unconfigured default dir cannot poison isolated bot state dirs.
  2. Surface a warning in --channels sessions when the poller is not spawned due to a needs-auth cache hit (and ideally re-validate auth against the actual resolved state dir instead of trusting the cache).

Either one alone would have prevented or immediately revealed this failure; both together would be ideal.

Related (searched, not duplicates)

  • #80635 — needs-auth cache keyed by server name poisoned by claude mcp list blocking a later --mcp-config run. Same underlying "cache keyed too coarsely" defect, but different trigger (mcp list vs headless -p) and different victim (MCP tool availability vs the channels poller, which fails silently).
  • #78220 — stale needs-auth flag keyed by server name, not cleared by mcp remove.
  • #76449 — plugin stdio MCP server silently classified "needs auth" with stderr discarded (Cowork).

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗