Skip to content

[bug] Coding tab's offline state is unrecoverable — it hides the only control that could clear it #241

Description

@serge-ivo

What happens

In agents/coder/web/src/CodingTab.tsx, runnerOnline starts null and has exactly two writers,
both of which require a session to already exist:

  • :207 — inside pollStatuses, which begins:
    const activeSessions = sessionsRef.current.filter((s) => s.status === "active");
    if (!activeSessions.length) return;                      // :193-194
    and is itself gated: usePolling(pollStatuses, 3000, hasActiveSessions && !openSession) (:213).
  • :256 — inside the capture poll for an open session.

So the flag can only ever change while a session is live. Now look at what it gates (:840):

) : runnerOnline === false ? (
    <p>Your machine isn't connected.</p>
    <code>pags up</code>
) : (
    <p>No session running.</p>
    <button onClick={() => startSession(solo.id)}>Start a session</button>
)

The offline state replaces the "Start a session" button. Once runnerOnline is false and the
sessions end, nothing can ever set it back to true — because that needs a session, and the only
control that starts one is no longer rendered.

How a user reaches it

Exactly the ordinary way: the runner drops (laptop sleeps, network blip, pags up restarted) while
a session is open. A capture returns runnerConnected: false. The session then ends or errors.
Bring the runner back and the Coding tab still insists your machine isn't connected, and offers no
way forward but the pags up you are already running.

Observed shape on a live instance (5219a03a, FWS Repo Coder) on 2026-08-06: runner genuinely
down ~00:02, 13 sessions all ended/error, activeSessions: 0, runner healthy again by 01:09 —
coding_diagnostics reporting runnerOnline: true, relayConnected: true the whole time the tab
would have been claiming otherwise.

It is escapable (CodingTab is keyed by instanceId, so navigating to another agent and back
remounts it to null, as does a reload) — but nothing tells the user that, and the message they
are given is both wrong and un-actionable.

Second, related problem

CodingTab never calls /v1/instances/:id/runtime/status — zero references. That is the
authoritative relay check the header dot and Settings both use. So the same screen can show a green
header dot and a "your machine isn't connected" body, sourced from two different signals, one of
which is a cached side effect of a poll that is no longer running.

Suggested shape

  • Never let a stale false outlive the sessions it came from: reset to null when the active-session
    set empties, or re-check on an interval that does not depend on a session existing.
  • Source it from /runtime/status (relay.connected) like every other consumer, so the Coding tab
    cannot disagree with the header.
  • Regardless: always render "Start a session". Showing the runner warning alongside it is
    useful; showing it instead is what makes the state unrecoverable.

Verification

  • With runnerOnline false and no active sessions, "Start a session" is still offered.
  • Runner returns while the tab sits open with no session → the warning clears without a remount.
  • Header dot and Coding tab body never disagree about connectivity.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions