[BUG] Telegram channel plugin: messages not delivered to Claude Code session when spawned via --channels in Docker
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?
When running Claude Code inside a Docker container with --channels plugin:telegram@claude-plugins-official, the Telegram plugin starts and polls correctly, but inbound messages are never delivered to the Claude Code session.
The same plugin works perfectly when run manually outside --channels. The bun subprocess is spawned and visible in ps aux as a child of claude, but messages from the bot never appear in the session, and Claude never responds back through Telegram.
Diagnostic findings:
When the plugin is run manually:
bun run /root/.claude/plugins/cache/claude-plugins-official/telegram/0.0.6/server.ts
It correctly polls Telegram and prints valid JSON-RPC notifications to stdout:
telegram channel: polling as @abdouIAbot
{"method":"notifications/claude/channel","params":{"content":"test123","meta":{"chat_id":"748329100","message_id":"35","user":"748329100","user_id":"748329100","ts":"2026-04-27T17:38:13.000Z"}},"jsonrpc":"2.0"}
This proves: token is valid, bot is reachable, plugin code works, sender is correctly allowlisted, and messages reach the plugin.
When spawned via --channels, the bun process is alive (visible in ps aux as child of claude PID), but /proc/<bun_pid>/fd/2 (stderr) returns empty, and no messages reach the Claude Code session — even though the same setup just worked manually moments before.
This suggests the stdio pipe between Claude Code and the spawned plugin subprocess is not being correctly read in containerized environments, or there is a buffering/PTY issue specific to Docker.
Workarounds attempted (none worked):
- Setting
PATHexplicitly in Dockerfile (ENV PATH="/root/.bun/bin:...") - Symlinking
bunto/usr/local/bin/bun - Adding the plugin as a regular MCP server in
~/.claude.json(server connects via/mcpbut messages are not pushed to the session —--channelsis the only way) - Killing all
bun/claudeprocesses and restarting fresh - Full container rebuild from scratch
- Installing
procpsfor proper process inspection
What Should Happen?
After running claude --channels plugin:telegram@claude-plugins-official, when a paired Telegram user sends a DM to the bot:
- The message should appear in the Claude Code session as a
<channel source="telegram" chat_id="..." ...>...</channel>block - Claude should be able to call the
replytool to respond back via the bot - Permission prompts (Bash, Write, etc.) should be relayed to Telegram for remote approval
Currently none of this happens — the session shows "Listening for channel messages" but inbound events from the plugin never surface.
Error Messages/Logs
Claude Code session shows:
Listening for channel messages from: plugin:telegram@claude-plugins-official
Experimental · inbound messages will be pushed into this session, this carries prompt injection risks.
Restart Claude Code without --channels to disable.
But no inbound message ever appears, despite the bun subprocess being alive.
When the plugin is run manually (outside --channels), stdout shows expected JSON-RPC events:
telegram channel: polling as @abdouIAbot
{"method":"notifications/claude/channel","params":{...},"jsonrpc":"2.0"}
When run via --channels, /proc/<bun_pid>/fd/2 is empty and no events reach Claude Code.
No errors are logged anywhere (~/.claude/logs/ doesn't exist, /tmp/claude-debug.log is empty).
Steps to Reproduce
- Build a Docker container with Node 22 bookworm-slim, Bun, and Claude Code (Dockerfile below)
- Start the container and
docker exec -itinto it - Authenticate Claude Code via claude.ai login
- Install the plugin:
/plugin install telegram@claude-plugins-official - Configure:
/telegram:configure <bot_token> - Start Claude Code with channels:
claude --channels plugin:telegram@claude-plugins-official - DM the bot from Telegram, get pairing code
- Pair:
/telegram:access pair <code>then/telegram:access policy allowlist - Restart:
claude --channels plugin:telegram@claude-plugins-official - Send any DM to the bot from the paired Telegram account
- Observe: "Listening for channel messages" shown in Claude Code, bun process running, but no message ever appears in the session
Container setup:
Dockerfile:
\\\dockerfile\
FROM node:22-bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
curl ca-certificates git unzip tini procps \
&& rm -rf /var/lib/apt/lists/*
RUN curl -fsSL https://bun.sh/install | bash
RUN ln -sf /root/.bun/bin/bun /usr/local/bin/bun \
&& ln -sf /root/.bun/bin/bunx /usr/local/bin/bunx
ENV PATH="/root/.bun/bin:/usr/local/bin:/usr/local/sbin:/usr/sbin:/usr/bin:/sbin:/bin"
RUN npm install -g @anthropic-ai/claude-code
WORKDIR /workspace
ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["bash"]
\\
docker-compose.yml:
\\\`yaml
services:
claude-code:
build: .
container_name: claude-code
restart: unless-stopped
stdin_open: true
tty: true
working_dir: /workspace
volumes:
- /root/claude-workspace:/workspace
- claude-config:/root/.claude
- claude-home:/root
environment:
- TERM=xterm-256color
volumes:
claude-config:
claude-home:
\\\`
Claude Model
Sonnet (default)
Is this a regression?
Yes, this worked in a previous version
Last Working Version
First time setting up — partially worked once during initial pairing/test, then stopped.
Claude Code Version
2.1.119
Platform
Other
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Xterm
Additional Information
Host environment: Hostinger VPS running Ubuntu, Docker standalone with Traefik reverse proxy managed by Portainer. The Claude Code container is on a separate dedicated network.
Authentication: claude.ai login (Pro plan, account: [redacted])
Bun version: Latest stable (installed from https://bun.sh/install during build)
Plugin version: telegram@claude-plugins-official v0.0.6, installed via /plugin install in user scope.
Important detail: the plugin's server.ts includes orphan-watchdog logic at line ~670 that auto-shutdowns when process.ppid !== bootPpid or stdin is destroyed. The plugin process stays alive for many minutes when monitored, so this is not the auto-shutdown firing — Claude Code simply doesn't seem to be reading the plugin's stdout despite the pipe presumably being open.
I am happy to provide additional diagnostics, run debug builds, or test patches.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗