{"record":{"id":"e7d57e23ece75ad7","repo":"Hmbown/CodeWhale","slug":"turn-turn-id-has-an-indeterminate-dynamic-tool-r","errorCode":null,"errorMessage":"Turn {turn_id} has an indeterminate dynamic-tool receipt; refusing to publish turn completion","messagePattern":"Turn (.+?) has an indeterminate dynamic-tool receipt; refusing to publish turn completion","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_threads.rs","lineNumber":4352,"sourceCode":"        claim: ClaimedDynamicToolSettlement,\n        timeout: Duration,\n    ) -> Result<()> {\n        let ack = self\n            .spawn_dynamic_tool_settlement(claim, DynamicToolTerminalOutcome::Timeout { timeout });\n        Self::await_dynamic_tool_settlement(ack).await?;\n        Ok(())\n    }\n\n    async fn settle_dynamic_tools_for_terminal_turn(\n        &self,\n        thread_id: &str,\n        turn_id: &str,\n    ) -> Result<()> {\n        loop {\n            let (claims, mut settling, indeterminate) =\n                self.claim_or_watch_pending_dynamic_tools_for_turn(thread_id, turn_id);\n            if indeterminate {\n                bail!(\n                    \"Turn {turn_id} has an indeterminate dynamic-tool receipt; refusing to publish turn completion\"\n                );\n            }\n            if claims.is_empty() && settling.is_empty() {\n                return Ok(());\n            }\n\n            let mut first_error = None;\n            for claim in claims {\n                let ack = self.spawn_dynamic_tool_settlement(\n                    claim,\n                    DynamicToolTerminalOutcome::Canceled {\n                        reason: \"turn_terminal\",\n                        terminal: true,\n                    },\n                );\n                if let Err(error) = Self::await_dynamic_tool_settlement(ack).await\n                    && first_error.is_none()","sourceCodeStart":4334,"sourceCodeEnd":4370,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/runtime_threads.rs#L4334-L4370","documentation":"settle_dynamic_tools_for_terminal_turn found at least one dynamic tool call for this (thread_id, turn_id) with entry.indeterminate == true, so it refuses to publish turn completion. An indeterminate call may already have a terminal receipt in the JSONL log; completing the turn on top of that could freeze an ambiguous state into history.","triggerScenarios":"A turn ends (normally or via cancellation) after one of its dynamic tool calls failed settlement with a failed rollback (mark_dynamic_tool_claim_indeterminate). The loop at runtime_threads.rs:4344-4360 checks before settling or publishing.","commonSituations":"Storage failure during a tool-result append mid-turn, then the turn finishes; crash-recovery resuming a turn with a tombstoned call; cancellation racing a flaky disk.","solutions":["Inspect the thread's JSONL log for terminal events of the turn's dynamic tool calls to establish what actually committed","Repair the storage fault, then reconcile the turn state via runtime restart / storage reconciliation instead of forcing completion","If the tombstone is provably stale (no terminal line exists), have the operator clear it - do not add a code path that bypasses the guard","Keep the evidence: this error marks a genuine data-integrity incident"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Turn completion blocked by an indeterminate tool call: escalate.\nmatch manager.complete_turn(thread_id, turn_id).await {\n    Ok(_) => Ok(()),\n    Err(e) if e.to_string().contains(\"indeterminate dynamic-tool receipt\") => {\n        tracing::error!(%thread_id, %turn_id, \"turn frozen; inspect Runtime storage\");\n        Err(e)\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Ensure every dynamic tool call reaches a terminal outcome (result, timeout, or turn-end cancellation) before the turn closes","Fix storage faults immediately; indeterminate tombstones come from failed rollbacks","Never force-complete a frozen turn in code; reconcile storage with an operator"],"tags":["dynamic-tools","turn-lifecycle","data-integrity","fail-closed","runtime"],"backgroundTag":"indeterminate-write-state","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}