Hmbown/CodeWhale · error
thread message for cannot be executed here…
Error message
thread message for {thread_id} cannot be executed here: Runtime::handle_thread does not run turns. Send it through the app-server runtime bridge (POST /v1/threads/{id}/turns). What it means
Runtime::handle_thread in crates/core is bookkeeping-only: it owns thread metadata and persistence, never turn execution. Receiving ThreadRequest::Message on this runtime means a caller tried to run a model turn through the wrong engine; instead of faking an 'accepted' response it returns this error directing the caller to the app-server runtime bridge endpoint POST /v1/threads/{id}/turns.
Solutions
- Route the message through the app-server runtime bridge: POST to /v1/threads/{thread_id}/turns instead of invoking Runtime::handle_thread with ThreadRequest::Message.
- Update your client integration to use the app-server's thread-turn endpoint rather than the core bookkeeping API.
- If you only need thread bookkeeping (metadata/persistence), keep using handle_thread but do not send Message variants through it.
- If you believe you are already going through the bridge, verify which runtime handle your client is bound to; you are likely holding the core runtime reference directly.
Example fix
// before
core_runtime.handle_thread(ThreadRequest::Message { thread_id, message })?;
// after
app_server.post(&format!("/v1/threads/{thread_id}/turns"), &turn_payload)?; Defensive patterns
Strategy: try-catch
Validate before calling
// Before sending, ensure the request targets the app-server bridge, not core runtime
if let ThreadRequest::Message { thread_id, .. } = req {
let url = format!("{app_server_base}/v1/threads/{thread_id}/turns");
return http.post(&url).json(&turn_payload).send();
} Type guard
fn is_bookkeeping_only(req: &ThreadRequest) -> bool {
matches!(req, ThreadRequest::Message { .. }) // must not go to handle_thread
} Try / catch
match runtime.handle_thread(req) {
Err(e) if e.to_string().contains("does not run turns") => {
// reroute through the app-server runtime bridge
bridge_post_turns(req)?;
}
other => other?,
} Prevention
- Always route turn execution through POST /v1/threads/{id}/turns on the app-server, never through the core runtime handle.
- Keep a single client wrapper that owns the bridge URL so callers cannot grab a core runtime handle directly.
- In tests, use the bridge client rather than Runtime::handle_thread for Message requests.
When it happens
Trigger: Sending a ThreadRequest::Message (a turn/message execution request for the given thread_id) directly into the core Runtime's handle_thread instead of routing it through the app-server HTTP runtime bridge (POST /v1/threads/{id}/turns).
Common situations: A client or plugin called the core runtime API directly instead of the app-server; an internal refactor moved message routing off the bridge; tooling replayed a message against the persistence runtime during recovery or tests.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Automation admission execution ownership is unverified or…
- bounded fragment module must expose…
- Cannot continue agent
- cannot discard a loaded Runtime thread
- cannot discard a Runtime thread that owns turns
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/340401b15e85ab73.
Report an issue: GitHub.
Appendix: source
Thrown at crates/core/src/lib.rs:1313
threads: Vec::new(),
goal: None,
model: None,
model_provider: None,
cwd: None,
approval_policy: None,
sandbox: None,
events: Vec::new(),
data: json!({}),
})
}
// A thread message is a *turn*, and this type is not the turn
// engine — it owns thread bookkeeping and persistence only. The
// app-server routes messages through its runtime bridge
// (`POST /v1/threads/{id}/turns` on the runtime API) and never
// reaches this arm. Returning an error rather than a canned
// "accepted" keeps any other caller from mistaking bookkeeping
// for execution.
ThreadRequest::Message { thread_id, .. } => Err(anyhow!(
"thread message for {thread_id} cannot be executed here: \
Runtime::handle_thread does not run turns. Send it through the \
app-server runtime bridge (POST /v1/threads/{{id}}/turns)."
)),
}
}
/// Evaluates execution policy and dispatches a tool call.
pub async fn invoke_tool(
&self,
call: ToolCall,
approval_mode: AskForApproval,
cwd: &Path,
) -> Result<Value> {
let fallback_cwd = cwd.display().to_string();
let (command, policy_cwd, execution_kind) = call.execution_subject(&fallback_cwd);
let policy_tool = match &call.payload {
ToolPayload::LocalShell { .. } => "exec_shell",View on GitHub (pinned to 73e0f67d83)