{"record":{"id":"03bb829ce9a5daf4","repo":"openai/codex","slug":"core-failed-to-submit-queued-user-message-0","errorCode":null,"errorMessage":"Core failed to submit queued user message: {0}","messagePattern":"Core failed to submit queued user message: (.+?)","errorType":"exception","errorClass":"QueueServiceError","httpStatus":null,"severity":"error","filePath":"codex-rs/ext/queue/src/service.rs","lineNumber":54,"sourceCode":"use tokio::sync::broadcast::error::TryRecvError;\nuse uuid::Uuid;\n\n/// One user message waiting to start on its thread.\n#[derive(Clone, Debug, PartialEq)]\npub struct QueuedItem {\n    pub id: String,\n    pub input: TurnInput,\n}\n\n#[derive(Debug, Error)]\npub enum QueueServiceError {\n    #[error(\"queue storage failed: {0}\")]\n    Storage(#[from] ThreadStoreError),\n    #[error(\"queued submission payload is invalid: {0}\")]\n    InvalidPayload(#[from] serde_json::Error),\n    #[error(\"local queued attachment is invalid: {0}\")]\n    InvalidAttachment(#[from] std::io::Error),\n    #[error(\"Core failed to submit queued user message: {0}\")]\n    CoreSubmissionError(#[from] CodexErr),\n    #[error(\"only user input can be added to the user-message queue\")]\n    InvalidInput,\n    #[error(\n        \"queued user input exceeds the maximum length of {MAX_USER_INPUT_TEXT_CHARS} characters ({actual_chars} provided)\"\n    )]\n    InputTooLarge { actual_chars: usize },\n}\n\n#[derive(Clone)]\npub struct QueuedItemService {\n    queue: Arc<dyn QueueStore>,\n    thread_manager: Weak<ThreadManager>,\n    event_sink: Arc<dyn ExtensionEventSink>,\n    dispatch_locks: Arc<StdMutex<HashMap<ThreadId, Weak<Mutex<()>>>>>,\n    resumed_threads: Arc<StdMutex<HashSet<ThreadId>>>,\n}\n","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/ext/queue/src/service.rs#L36-L72","documentation":"QueueServiceError::CoreSubmissionError wraps the CodexErr returned by CodexThread::start_turn_if_idle when the queue extension replays a stored user message on an idle thread (codex-rs/ext/queue/src/service.rs, start() around line 390). The queue persists user messages typed while a thread is busy and submits them once the thread goes idle; this error means codex-core itself refused that submission - typically because the thread is in a state that cannot accept a turn (shutting down, interrupted, already running) or the turn was rejected at the core level. The inner CodexErr carries the real reason; the background auto-dispatch path (dispatch_if_idle) logs the same failure as 'core could not start queued user input' instead of returning it.","triggerScenarios":"Calling QueuedItemService::start(&thread, queued_item_id, trace) and having start_turn_if_idle return Err: the thread is Running/Interrupted/Shutdown at submission time (status raced between the idle check and the submit), the thread session is being torn down, or core rejects the stored TurnInput. The same failure is also logged (not returned) by the on_thread_idle auto-dispatch path.","commonSituations":"Cancelling or shutting down a thread while queued messages still exist; a queued payload persisted by an older codex version being replayed after an upgrade; concurrent UI actions (interrupt plus send-to-queue) racing the idle lifecycle.","solutions":["Match on the inner CodexErr (the {0} in the message) - it names the actual rejection; fix that condition first","Check thread.agent_status().await immediately before start() and only submit when Idle - submissions against Interrupted/Shutdown threads will keep failing","Retry start() after the thread settles back to Idle if the failure was a Running race","If the queued item is stale (old payload schema), delete it with service.delete(thread_id, item_id) and let the user resend"],"exampleFix":"// before\nlet submission = queue.start(&thread, Some(item_id), None).await?; // CoreSubmissionError\n\n// after\nuse codex_protocol::protocol::AgentStatus;\nif !matches!(thread.agent_status().await, AgentStatus::Idle) {\n    return Ok(None); // thread cannot accept a queued turn right now\n}\nmatch queue.start(&thread, Some(item_id), None).await {\n    Ok(submission) => Ok(Some(submission)),\n    Err(QueueServiceError::CoreSubmissionError(err)) => {\n        tracing::warn!(%err, \"queued turn rejected; keeping it queued\");\n        Ok(None)\n    }\n    Err(err) => Err(err.into()),\n}","handlingStrategy":"try-catch","validationCode":"use codex_protocol::protocol::AgentStatus;\n// only attempt a queued start when the thread can accept a turn\nif !matches!(thread.agent_status().await, AgentStatus::Idle) {\n    return; // Running/Interrupted/Shutdown make core refuse the queued turn\n}","typeGuard":null,"tryCatchPattern":"match queue.start(&thread, Some(item_id), None).await {\n    Ok(submission) => { /* inspect StartIfIdleSubmission::Started vs NotSubmitted */ }\n    Err(QueueServiceError::CoreSubmissionError(err)) => {\n        // keep the item queued; the inner CodexErr is the real cause\n        tracing::warn!(%err, \"queued turn rejected\");\n    }\n    Err(err) => { /* storage/serialization failures */ }\n}","preventionTips":["Treat StartIfIdleSubmission::NotSubmitted (benign) and Err (this error) differently","Drain or delete queued items before intentionally shutting a thread down","Never assume a thread observed idle stays idle - re-check agent_status right before start()"],"tags":["queue","codex-core","turn-submission","rust"],"backgroundTag":"turn-submission-failed","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}