The telep-mainframe master control-plane agent (control-plane.serviceclaude remote-control) threw an OAuth error in the Claude app; root-caused to a ~/.claude/.credentials.json with every token zeroed, fixed by an interactive claude login on the box.

Fix requires a REAL terminal on the box

claude login cannot be run through Claude Code’s ! prefix or plain ssh (no TTY). It must be an interactive ssh -t session. Restarting the service does NOT fix zeroed creds — only re-login does.

Symptoms

  • Claude app showed an OAuth error for the control-plane session.
  • control-plane.service reported active/running — misleadingly healthy.
  • The agent pane logged constant Reconnected after Ns churn (crash / auto-restart loop).

Root cause

~/.claude/.credentials.json on telep-mainframe had all tokens zeroed (file had been rewritten 07:48 that morning):

  • claudeAiOauth.accessToken = empty string (len 0)
  • claudeAiOauth.refreshToken = empty string (len 0)
  • claudeAiOauth.expiresAt = 0
  • every mcpOAuth.*.accessToken (Supabase, Gmail, Drive, GitHub) = empty
  • subscriptionType=max metadata was still intact

Because refreshToken was empty, silent refresh was impossible — a full re-login was the only path. The service, being on Restart=always, just churned in a reconnect loop against dead creds (looked “active” the whole time).

Fix — interactive claude login on the box

Run the login in a real terminal with a TTY, on the host:

ssh -t levander@telep-mainframe '~/.local/bin/claude login'
# open the printed URL in a browser, approve, paste the code back

The control-plane agent shares the same per-user ~/.claude/.credentials.json, so once login rewrites the file with valid tokens, the service (already in its auto-restart loop) picks up the new creds automatically — no systemctl restart needed.

Verified after login

  • accessToken / refreshToken len 108 (populated)
  • expiresAt valid, ~8 h out
  • control-plane.serviceactive (running)
  • agent pane → ✔︎ Connected · obsidian · main

Gotchas worth remembering

For Agents

  1. claude login needs a TTY. It fails through Claude Code’s ! prefix or plain non-interactive ssh with OAuth session expired and could not be refreshed. Use ssh -t levander@telep-mainframe '~/.local/bin/claude login' from a real terminal.
  2. Restarting control-plane.service on empty creds does nothing — it just re-enters the crash/auto-restart loop. The real fix is the interactive login; no restart is needed afterward (auto-restart picks up the new creds).
  3. Off-LAN reachability + slow relay. When away from the home LAN, telep-mainframe’s LAN IP 192.168.1.123 and hostname are unreachable — only the relayed Tailscale path 100.115.209.87 works, and it is slow (~800 ms RTT via a DERP relay). SSH needs a long ConnectTimeout (30 s+) or it times out during banner exchange; short 8 s timeouts fail even though the box is up.