Hmbown/CodeWhale · error · anyhow::Error
User-input request '{input_id}' has an indeterminate termina
Error message
User-input request '{input_id}' has an indeterminate terminal receipt; inspect Runtime storage before retrying What it means
submit_user_input claimed input_id and got PendingUserInputClaim::Indeterminate: a previous answer attempt for this request hit a failed append whose rollback also failed, leaving entry.indeterminate = true. The runtime refuses to accept or re-deliver an answer whose durable receipt cannot be established, because either action could duplicate or disclose a response.
Source
Thrown at crates/tui/src/runtime_threads.rs:3284
&self,
thread_id: &str,
input_id: &str,
response: crate::tools::user_input::UserInputResponse,
) -> Result<bool> {
let engine = {
let active = self.active.lock().await;
let Some(state) = active.engines.get(thread_id) else {
bail!("thread '{thread_id}' not found");
};
state.engine.clone()
};
let request = match self.claim_pending_user_input(thread_id, input_id) {
PendingUserInputClaim::Claimed(request) => request,
PendingUserInputClaim::Missing | PendingUserInputClaim::Settling => {
return Ok(false);
}
PendingUserInputClaim::Indeterminate => {
bail!(
"User-input request '{input_id}' has an indeterminate terminal receipt; inspect Runtime storage before retrying"
);
}
};
// This child task deliberately outlives the HTTP future. Once a
// request is claimed, client disconnect/cancellation cannot strand it
// between durable acceptance and engine delivery.
let manager = self.clone();
let thread_id = thread_id.to_string();
tokio::spawn(async move {
manager
.settle_claimed_user_input(
&thread_id,
Some(engine),
request,
UserInputTerminalOutcome::Answered(response),
)View on GitHub (pinned to 0c42157ee5)
Solutions
- Do not retry the same input_id - the tombstone is intentional
- Inspect the runtime store's JSONL log for the user_input answer receipt to learn whether the first attempt committed
- Fix the underlying storage problem, then restart/reconcile the runtime so pending state is rebuilt from disk
- Design the client to treat 'unknown outcome' as an operator-visible incident, not a retryable error
Defensive patterns
Strategy: try-catch
Try / catch
// The indeterminate bail is terminal for this input_id - escalate.
match manager.submit_user_input(thread_id, input_id, response).await {
Ok(accepted) => Ok(accepted),
Err(e) if e.to_string().contains("indeterminate terminal receipt") => {
tracing::error!(%input_id, "answer receipt unknown; inspect Runtime storage");
Err(e)
}
Err(e) => Err(e),
} Prevention
- Treat submit timeouts on flaky links as 'outcome unknown' - do not blind-retry the same input_id
- Keep storage reliable so answer appends and their rollbacks succeed
- Persist the input_id so an incident can be reconciled against the JSONL log later
When it happens
Trigger: Retrying submit_user_input with the same input_id after the first attempt's spawned delivery task failed non-retry-safely (mark_pending_user_input_indeterminate ran). Guard at runtime_threads.rs:3284-3287, flag logic at :2993-3013.
Common situations: Storage I/O failure during the first submit; automatic client retry after an HTTP timeout where the server may have already committed; process kill mid-append.
Related errors
- User-input request '{}' has an indeterminate terminal receip
- Dynamic tool call '{call_id}' has an indeterminate terminal
- Turn {turn_id} has an indeterminate dynamic-tool receipt; re
- Dynamic tool call '{}' is already pending
- thread '{thread_id}' not found
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/2cccba615cc61490.
Report an issue: GitHub.