openai/codex · error · QueueServiceError
Core failed to submit queued user message: {0}
Error message
Core failed to submit queued user message: {0} What it means
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.
Source
Thrown at codex-rs/ext/queue/src/service.rs:54
use tokio::sync::broadcast::error::TryRecvError;
use uuid::Uuid;
/// One user message waiting to start on its thread.
#[derive(Clone, Debug, PartialEq)]
pub struct QueuedItem {
pub id: String,
pub input: TurnInput,
}
#[derive(Debug, Error)]
pub enum QueueServiceError {
#[error("queue storage failed: {0}")]
Storage(#[from] ThreadStoreError),
#[error("queued submission payload is invalid: {0}")]
InvalidPayload(#[from] serde_json::Error),
#[error("local queued attachment is invalid: {0}")]
InvalidAttachment(#[from] std::io::Error),
#[error("Core failed to submit queued user message: {0}")]
CoreSubmissionError(#[from] CodexErr),
#[error("only user input can be added to the user-message queue")]
InvalidInput,
#[error(
"queued user input exceeds the maximum length of {MAX_USER_INPUT_TEXT_CHARS} characters ({actual_chars} provided)"
)]
InputTooLarge { actual_chars: usize },
}
#[derive(Clone)]
pub struct QueuedItemService {
queue: Arc<dyn QueueStore>,
thread_manager: Weak<ThreadManager>,
event_sink: Arc<dyn ExtensionEventSink>,
dispatch_locks: Arc<StdMutex<HashMap<ThreadId, Weak<Mutex<()>>>>>,
resumed_threads: Arc<StdMutex<HashSet<ThreadId>>>,
}
View on GitHub (pinned to 339751715c)
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
Example fix
// before
let submission = queue.start(&thread, Some(item_id), None).await?; // CoreSubmissionError
// after
use codex_protocol::protocol::AgentStatus;
if !matches!(thread.agent_status().await, AgentStatus::Idle) {
return Ok(None); // thread cannot accept a queued turn right now
}
match queue.start(&thread, Some(item_id), None).await {
Ok(submission) => Ok(Some(submission)),
Err(QueueServiceError::CoreSubmissionError(err)) => {
tracing::warn!(%err, "queued turn rejected; keeping it queued");
Ok(None)
}
Err(err) => Err(err.into()),
} Defensive patterns
Strategy: try-catch
Validate before calling
use codex_protocol::protocol::AgentStatus;
// only attempt a queued start when the thread can accept a turn
if !matches!(thread.agent_status().await, AgentStatus::Idle) {
return; // Running/Interrupted/Shutdown make core refuse the queued turn
} Try / catch
match queue.start(&thread, Some(item_id), None).await {
Ok(submission) => { /* inspect StartIfIdleSubmission::Started vs NotSubmitted */ }
Err(QueueServiceError::CoreSubmissionError(err)) => {
// keep the item queued; the inner CodexErr is the real cause
tracing::warn!(%err, "queued turn rejected");
}
Err(err) => { /* storage/serialization failures */ }
} Prevention
- 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()
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- queue storage failed: {0}
- queued submission payload is invalid: {0}
- local queued attachment is invalid: {0}
- only user input can be added to the user-message queue
- queued user input exceeds the maximum length of {MAX_USER_IN
AI-assisted analysis of openai/codex@339751715c (2026-08-25).
Data as JSON: /api/errors/03bb829ce9a5daf4.
Report an issue: GitHub.