-
Notifications
You must be signed in to change notification settings - Fork 0
Troubleshooting
The failures people actually hit, grouped by where they bite.
"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.
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 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 F3 — F3 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.
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>:claudeanswer 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.
"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.
/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.
⚠ 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.
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 it — claude-jam host --attach, or the
claude-jam join ws://127.0.0.1:<port> … --host line the launcher printed.
Host wants a clean restart — claude-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.
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.