Hmbown/CodeWhale · warning · Error

No active turn

Error message

No active turn

What it means

steerTurn() in the mobile UI requires both a selected thread and a live turn; it throws when state.threadId or state.activeTurnId is falsy. activeTurnId is only assigned from a createTurn response (res.turn?.id) and lives in page memory, so it does not survive a reload.

Source

Thrown at crates/tui/src/runtime_mobile.html:536

      if (!state.threadId) await newThread();
      const prompt = $("prompt").value.trim();
      if (!prompt) return;
      const res = await api("/v1/threads/" + encodeURIComponent(state.threadId) + "/turns", {
        method: "POST",
        body: JSON.stringify({
          prompt,
          allow_shell: $("allow-shell").checked,
          trust_mode: false,
          auto_approve: $("auto-approve").checked
        })
      });
      state.activeTurnId = res.turn?.id || state.activeTurnId;
      $("prompt").value = "";
      await loadThreads();
    }

    async function steerTurn() {
      if (!state.threadId || !state.activeTurnId) throw new Error("No active turn");
      const prompt = $("prompt").value.trim();
      if (!prompt) return;
      await api(
        "/v1/threads/" + encodeURIComponent(state.threadId) +
        "/turns/" + encodeURIComponent(state.activeTurnId) + "/steer",
        { method: "POST", body: JSON.stringify({ prompt }) }
      );
      $("prompt").value = "";
    }

    async function interruptTurn() {
      if (!state.threadId || !state.activeTurnId) throw new Error("No active turn");
      await api(
        "/v1/threads/" + encodeURIComponent(state.threadId) +
        "/turns/" + encodeURIComponent(state.activeTurnId) + "/interrupt",
        { method: "POST", body: "{}" }
      );
    }

View on GitHub (pinned to 8880682c63)

Solutions

  1. Send a new prompt first (createTurn) to establish state.activeTurnId
  2. Reload the thread list and re-select the running turn before steering
  3. If responses lack turn.id, verify the runtime API version matches the shipped HTML

Example fix

// before
async function steerTurn() {
  if (!state.threadId || !state.activeTurnId) throw new Error('No active turn');
  // ...
}

// after - keep the steer button disabled until a turn is live
function refreshTurnControls() {
  const live = Boolean(state.threadId && state.activeTurnId);
  $('steer').disabled = !live;
  $('interrupt').disabled = !live;
}
Defensive patterns

Strategy: validation

Validate before calling

const hasActiveTurn = () => Boolean(state.threadId && state.activeTurnId);
if (!hasActiveTurn()) {
  toast('Start a turn first, then steer it');
  return;
}

Type guard

function hasActiveTurn(state) {
  return typeof state.threadId === 'string' && state.threadId.length > 0 &&
    typeof state.activeTurnId === 'string' && state.activeTurnId.length > 0;
}

Try / catch

try {
  await steerTurn();
} catch (err) {
  if (err.message === 'No active turn') { toast('No active turn to steer'); return; }
  throw err;
}

Prevention

When it happens

Trigger: Tapping Steer before any turn was created in this page session; after a page reload (in-memory state lost); when the createTurn response lacked turn.id so activeTurnId stayed unset.

Common situations: User reloads the mobile page mid-run and tries to steer; switching threads without starting a turn; an API response shape change dropping turn.id.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/f59e362a97ba0dd3. Report an issue: GitHub.