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
- Send a new prompt first (createTurn) to establish state.activeTurnId
- Reload the thread list and re-select the running turn before steering
- 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
- Disable steer/interrupt controls until a turn id is known
- Persist or re-fetch the active turn after page reloads
- Treat a missing turn.id in the createTurn response as a protocol error and log it
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
- ${detail || ("HTTP " + res.status)}
- ${response.status} ${response.statusText}
- This browser session is not authenticated. Restart `codewhal
- Runtime API request failed (${status}): ${message}
- Runtime API request failed (${status}): ${message}
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/f59e362a97ba0dd3.
Report an issue: GitHub.