Skip to content

Shell tab PTY killed on every tab switch — claude loses auth on each return #6

@JaredT6694

Description

@JaredT6694

Problem

Every time you switch away from the Shell tab and come back, you get a completely fresh bash session. If claude is running in that session, it's gone — and depending on OAuth token state, it asks for re-authentication. For Claude Code users, this means re-authenticating multiple times per day just from normal tab switching.

Root Cause

In src/server.ts, the WebSocket close handler unconditionally kills the PTY:

ws.on('close', () => {
  sessions.delete(sessionId);
  try { ptyProc.kill(); } catch { /* ignore */ }
});

There is no reconnect path — every new WebSocket connection spawns a fresh shell with a new session ID. The client-side code includes the comment "Sessions persist across mount/unmount (tab switching)", but the server does not honor that intent.

Impact

  • Any long-running process in the Shell tab (claude, a build, a server) is killed on every tab switch
  • Claude Code users must re-run claude each time they return to the tab; if the OAuth access token is near expiry, this triggers the full re-authentication flow
  • The "--- reconnected ---" message shown in the terminal gives the false impression that the session is preserved

Proposed Fix

Keep the PTY alive on WebSocket disconnect and reconnect to it on the next connection. A per-session timeout (e.g. 30 minutes) would handle cleanup for truly abandoned sessions:

// On disconnect: detach WebSocket but keep PTY alive
ws.on('close', () => {
  const session = sessions.get(sessionId);
  if (session) {
    session.ws = null;
    session.cleanupTimer = setTimeout(() => {
      try { session.pty.kill(); } catch { /* ignore */ }
      sessions.delete(sessionId);
    }, 30 * 60 * 1000);
  }
});

// On new connection: reuse existing session if available
wss.on('connection', (ws) => {
  const existingEntry = [...sessions.entries()].find(([, s]) => s.ws === null);
  if (existingEntry) {
    const [sessionId, session] = existingEntry;
    clearTimeout(session.cleanupTimer);
    session.ws = ws;
    // Reattach data handlers and send ready
    return;
  }
  // No existing session — spawn fresh
});

Workaround

Until this is fixed, users can add a tmux auto-attach to .bashrc gated on $TERM_PROGRAM=web-terminal. The web-terminal kills the PTY (tmux client), but the tmux server keeps running, so long-running processes survive tab switches:

# At the end of ~/.bashrc
if [ -z "$TMUX" ] && [ "$TERM_PROGRAM" = "web-terminal" ]; then
    exec tmux new-session -A -s main
fi

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions