Skip to content

Troubleshooting

Roy Padina edited this page Aug 29, 2026 · 3 revisions

Troubleshooting

The failures people actually hit, grouped by where they bite.

Getting in

"still retrying — the join URL changed" — it probably did. The host's --tunnel relay restarted and Cloudflare handed out a fresh random hostname. The host runs /join and sends the new line, or switches to --funnel, whose hostname never changes. Links minted before the respawn still work over their LAN address.

A knock never gets answered — knocks expire after 2 minutes, and at most 10 wait at once. The host has to be looking at their client. Ask them for an invite link instead.

"this jam is invite-only" — knocks are refused on purpose. Ask the host for a claude-jam invite <YourName> link.

A link is refused — the reason is printed: tampered, expired, revoked, used up, or the name is already connected. It then falls through to a knock, so the host can still wave you in. A cjam2_… link means "update claude-jam", not a broken link.

Two people with the same name — refused. Attribution is by name, so two live participants can never share one.

The screen

F2, Shift+Enter or the live view do nothing — you are running --basic, which is transcript-only. Drop the flag. (It is also chosen automatically when stdin is not a tty.)

The screen looks cropped or half empty — your terminal is smaller than the host's window; the dim — mirror: line says how much was cut. The host's own client keeps the window sized to their terminal, so a guest with a bigger terminal simply sees blank space.

You can see things from before you joined — yes: the daemon replays up to the host's --replay events of the transcript on connect, with a history above · live from here divider under it. The host can launch with --replay 0.

Colours moved since an earlier jam — v0.17 swapped one palette slot (a pale green that was too close to the green every client uses for itself). Colours are stable per name; that one slot is rose now.

[masked] where a value should be — the secret deny-list recognised a shape. The value is unchanged on disk and in claude's context; only the copy being shown to other people is masked. There is no per-line override. See Security Model.

F3 and the raw TUI

F3 does nothing — either you are a guest (F3 is host and loopback only), or your terminal sends a different F3 sequence. A guest who needs to answer a prompt wants /answer. A host can tmux attach instead — that is exactly what F3 does for them.

Stuck inside the TUI after F3F3 again detaches, and Ctrl-b d still does the same. A host running --tmux-socket default has only Ctrl-b d, because a bare F3 binding on the shared server would be their whole tmux.

"tmux attach -t claude-jam says there is no session" — claude-jam runs its own tmux server. The line is tmux -L claude-jam-<port> attach -t <name>:claude, and claude-jam sessions prints the exact one for every live jam.

Ctrl-b d does nothing — you launched claude-jam from inside another tmux, so the outer prefix eats the first Ctrl-b. Use Ctrl-b Ctrl-b d.

a or d lands in the message instead of answering — the single keys are armed only on an empty input line, so a message starting with d can never deny anybody. Esc re-arms them. A guest never has them at all.

The first run in a directory

Nothing happens after claude-jam host — in a directory Claude Code has never run in, claude shows "do you trust the files in this folder?". The daemon answers it automatically before its first injection (moving off the "No, exit" default first), and waits up to 30 s for it. If it is still stuck, look:

tmux -L claude-jam-<port> attach -t <name>:claude

answer it, Ctrl-b d to come back.

command -v claude prints alias text, not a path — that is zsh. Ask for the binary: whence -p claude. And check what comes back: on a machine running cmux, whence -p claude can be a shim in $TMPDIR rather than claude. $HOME/.local/bin/claude is usually the real one, and is claude-jam's own first choice. JAM_CLAUDE=<path> pins it.

Messages and prompts

"couldn't confirm your message reached claude" — it was kept, byte for byte, and nothing was retyped or wiped. /outbox lists what is kept, /retry sends the newest again, gets the text back into your box.

A guest's /command did nothing — it is waiting for the host's /allow-cmd, or it hit the hard host-only list (/exit, /clear, /resume), or it expired after 2 minutes. The exact reason went to that person's own client as a ! line.

A guest's file or /export did nothing — waiting for /accept-file / /allow-export, or expired, or refused: a name with a path in it, over 20 MB (files) / 50 MB (transcript and offers), or a second one while the first is still in flight.

/answer says "nothing is waiting for a permission answer" — claude is not asking right now. The in the status row is the moment it works.

/answer says it cannot read numbered options — that prompt is not one the relay can drive (or what is on screen is not a prompt at all). The host answers that one with F3.

/answer says "claude's screen changed after you asked" — the prompt was answered or replaced between the request and the host's approval, so nothing was typed. Look at the screen and ask again.

A guest ran /cost, /status or /context and nobody was asked — correct. Exactly those three, exactly bare, are read-only and need no approval. /cost --json asks like anything else.

Nothing ever asks for permission at all — the host's Claude Code is running with "defaultMode": "bypassPermissions" or --dangerously-skip-permissions. There is then no prompt to relay and no approval anywhere. See Security Model.

Replies stop with a spend-limit message — the host's Claude account hit its usage limit. They can restart with --config-dir pointing at another profile.

Files

/paste says it is macOS-only — it is: it reads a PNG off the mac clipboard (pngpaste when installed, otherwise the built-in osascript). Elsewhere, save the image and /send <path>.

/diff says "not inside a git repository" — the host's --cwd is not in one. /files still works; it reports what tool calls named rather than what git sees.

/diff says "no unstaged changes" — the work is committed or staged. /diff shows only the unstaged working tree, capped at 120 lines.

/files says "no files yet" — nothing has read or edited a file that a tool call named. A file changed by a shell command inside a Bash call never appears there — that is what /diff is for.

Connection

⚠ stale 40s in the status row — no heartbeat pong for more than a couple of 30-second intervals. That link is degraded or gone; the client reconnects on its own, in tiers.

--funnel cannot start: … — the message names the one missing thing. Funnel needs the funnel node attribute granted for the tailnet in Access Controls, a connected Tailscale, and a CLI claude-jam can reach (--funnel-cli <path>). On macOS the App Store build of Tailscale.app is sandboxed and cannot change funnel configuration at all — its CLI answers The Tailscale GUI failed to start … (Tailscale.CLIError error 3.). The standalone build from tailscale.com can. --tunnel is always the fallback.

Sessions that will not go away

claude-jam end refuses — claude-jam ends only a session it created and can prove it created. The message names what did not line up. tmux kill-session -t <name> is the human's own call, not claude-jam's, and it should be an exact name (-t =name), never a pattern.

claude-jam sessions does not show a jam that is clearly running — it was started before v0.18, so it has no marker and no session.json; claude-jam treats it as none of its business. End that one with tmux kill-session -t <name>. A jam created by 0.18.0 is recognised — its older @jam-owned marker is still read.

A jam shows ! no-daemon — the tmux session is up but nothing answers on its port. claude-jam end <name> clears it out.

A jam shows ! orphan — the tmux session is gone and only the state directory is left. claude-jam clean removes exactly those, after listing what will go.

A jam shows ! no-session — no tmux session, but something still holds that port. claude-jam leaves it alone, because whatever holds the port is not its to remove.

A jam shows foreign — that name is somebody else's tmux session. It is shown and never touched.

Host wants their client back after closing itclaude-jam host --attach, or the claude-jam join ws://127.0.0.1:<port> … --host line the launcher printed.

Host wants a clean restartclaude-jam end, then claude-jam host … again; --resume <session-id> keeps the conversation.

Everybody's client said <Host> ended the jam and exited — it did end, on purpose. There is nothing to reconnect to; the host starts a new one.

Still stuck

MANUAL.md in the repo is the manual claude itself is given, so inside a jam you can simply ask: "how does this jam work?" — and get an accurate answer rather than a guess.

Next

Install · Hosting a Jam · Joining a Jam · Architecture

Clone this wiki locally