{"record":{"id":"f59e362a97ba0dd3","repo":"Hmbown/CodeWhale","slug":"no-active-turn","errorCode":null,"errorMessage":"No active turn","messagePattern":"No active turn","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"crates/tui/src/runtime_mobile.html","lineNumber":536,"sourceCode":"      if (!state.threadId) await newThread();\n      const prompt = $(\"prompt\").value.trim();\n      if (!prompt) return;\n      const res = await api(\"/v1/threads/\" + encodeURIComponent(state.threadId) + \"/turns\", {\n        method: \"POST\",\n        body: JSON.stringify({\n          prompt,\n          allow_shell: $(\"allow-shell\").checked,\n          trust_mode: false,\n          auto_approve: $(\"auto-approve\").checked\n        })\n      });\n      state.activeTurnId = res.turn?.id || state.activeTurnId;\n      $(\"prompt\").value = \"\";\n      await loadThreads();\n    }\n\n    async function steerTurn() {\n      if (!state.threadId || !state.activeTurnId) throw new Error(\"No active turn\");\n      const prompt = $(\"prompt\").value.trim();\n      if (!prompt) return;\n      await api(\n        \"/v1/threads/\" + encodeURIComponent(state.threadId) +\n        \"/turns/\" + encodeURIComponent(state.activeTurnId) + \"/steer\",\n        { method: \"POST\", body: JSON.stringify({ prompt }) }\n      );\n      $(\"prompt\").value = \"\";\n    }\n\n    async function interruptTurn() {\n      if (!state.threadId || !state.activeTurnId) throw new Error(\"No active turn\");\n      await api(\n        \"/v1/threads/\" + encodeURIComponent(state.threadId) +\n        \"/turns/\" + encodeURIComponent(state.activeTurnId) + \"/interrupt\",\n        { method: \"POST\", body: \"{}\" }\n      );\n    }","sourceCodeStart":518,"sourceCodeEnd":554,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/runtime_mobile.html#L518-L554","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nasync function steerTurn() {\n  if (!state.threadId || !state.activeTurnId) throw new Error('No active turn');\n  // ...\n}\n\n// after - keep the steer button disabled until a turn is live\nfunction refreshTurnControls() {\n  const live = Boolean(state.threadId && state.activeTurnId);\n  $('steer').disabled = !live;\n  $('interrupt').disabled = !live;\n}","handlingStrategy":"validation","validationCode":"const hasActiveTurn = () => Boolean(state.threadId && state.activeTurnId);\nif (!hasActiveTurn()) {\n  toast('Start a turn first, then steer it');\n  return;\n}","typeGuard":"function hasActiveTurn(state) {\n  return typeof state.threadId === 'string' && state.threadId.length > 0 &&\n    typeof state.activeTurnId === 'string' && state.activeTurnId.length > 0;\n}","tryCatchPattern":"try {\n  await steerTurn();\n} catch (err) {\n  if (err.message === 'No active turn') { toast('No active turn to steer'); return; }\n  throw err;\n}","preventionTips":["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"],"tags":["ui","client","state","runtime"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}