← All posts

My Agent Kept Signing Its Mail With Someone Else's Return Address

A tmux pane-id bug taught me to pin agent identity to my own process, not to whatever the system says is active.

  • multi-agent
  • tmux
  • claude-code
  • debugging
  • orchestration

I had a handful of Claude Code agents running side by side, each in its own tmux pane — think of tmux as a way to split one terminal window into separate tiled boxes, each running its own program. The agents talk to each other by dropping little messages (“done with the migration, over to you”) and signing each one with their pane id, the way you’d sign a letter with your return address so the reply comes back to you.

For a whole work cycle, the replies went to the wrong agent.

The agent living in pane %99 was signing every single message as %108. So when another agent sent back an acknowledgment — an “ack,” just a confirmation that it got the message — the ack sailed off to %108, a pane that had nothing to do with the conversation. Work stalled in a quiet, confusing way. Nothing crashed. Messages just kept missing their target, and I only caught it because I sat down and audited who was actually who.

Here’s the dumb, beautiful root cause. To find out its own pane id, the agent ran tmux display -p '#{pane_id}'. That looks like “tell me my pane id.” It isn’t. A bare tmux display reports the globally focused pane — whatever box happens to be highlighted right now, across the whole session. It’s like asking “who am I?” and the room answers with the name of whoever’s currently talking. If a human had clicked into pane %108, or another agent grabbed focus, every agent querying at that moment would swear it was %108.

The agents weren’t lying about their identity. They were reading it off the ambient state of the room instead of off themselves.

The fix took one word. tmux sets an environment variable, $TMUX_PANE, inside each pane’s own process — scoped to you, not to the room. So echo "$TMUX_PANE" returns %99 from inside %99, always, no matter who’s focused. Identity pinned to the process, not to a global question.

The two queries and why one lies give me the detail
# WRONG: returns the globally-active pane, whoever is focused
tmux display -p '#{pane_id}'

# RIGHT, option A: read the var scoped to your own process
echo "$TMUX_PANE"

# RIGHT, option B: force the target flag to your own pane
tmux display -p -t "$TMUX_PANE" '#{pane_id}'

The trap is that tmux display without -t defaults to the active pane, and in a single-human session that’s usually you — so it passes every manual test and only breaks under real concurrency. Same failure class hides in $$ vs pgrep, or reading the “current” session id. Anything that self-identifies for routing should read from a var the OS scoped to that exact process, then log it once at startup so you can diff self-reported identity against ground truth.

The transferable bit isn’t about tmux. It’s that a call phrased as “what am I?” can quietly answer “what’s active?” — and those match right up until they don’t, usually the moment two things run at once. Whenever an agent self-identifies for routing — by pane, by PID, by session — pin the identity to something scoped to that process, and cross-check it against the live artifact before you trust it. Ask the room who’s talking and you’ll route your mail to whoever’s loudest.