openai/codex · error · QueueServiceError

queued submission payload is invalid: {0}

Error message

queued submission payload is invalid: {0}

What it means

Wraps serde_json::Error via #[from] (codex-rs/ext/queue/src/service.rs:50-51). It has two directions: serializing a TurnInput when enqueueing or updating (service.rs:270 and 321, practically unreachable for well-formed values) and deserializing a stored payload back into TurnInput in queued_item_from_record (service.rs:560-566). In practice it fires on the read side: a queue record whose payload no longer matches the TurnInput schema, typically written by an older or newer binary. The dispatcher itself treats such records as garbage, discarding them with a warning (service.rs:419-426), but list(), update(), and start() propagate the error.

Source

Thrown at codex-rs/ext/queue/src/service.rs:50

use codex_thread_store::ThreadStoreError;
use thiserror::Error;
use tokio::sync::Mutex;
use tokio::sync::OwnedMutexGuard;
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>,

View on GitHub (pinned to 339751715c)

Solutions

  1. Run one version at a time against a given state database so writer and reader agree on the schema.
  2. Drain or delete stale queued items; the automatic dispatcher already discards invalid ones when the thread goes idle, and queue.delete(thread_id, item_id) removes specific rows.
  3. Read the serde error message; it names the field that failed to deserialize.
  4. For queues that must survive upgrades, add a migration or clear the thread queue table.

Example fix

// before
let items = service.list(thread_id).await?; // fails if any stored payload is stale

// after - isolate and drop unreadable rows (same policy as the dispatcher)
for record in store.list_page(thread_id, 0, MAX_QUEUE_ITEMS).await? {
    if serde_json::from_str::<TurnInput>(&record.payload).is_err() {
        tracing::warn!(id = %record.id, "dropping unreadable queued item");
        store.delete(thread_id, record.id).await?;
    }
}
let items = service.list(thread_id).await?;
Defensive patterns

Strategy: fallback

Validate before calling

// Rust - probe a stored payload before trusting it
fn payload_parses(payload: &str) -> bool {
    serde_json::from_str::<TurnInput>(payload).is_ok()
}

Try / catch

match service.list(thread_id).await {
    Ok(items) => { /* ... */ }
    Err(QueueServiceError::InvalidPayload(serde_err)) => {
        // fall back to store-level listing and drop rows that do not parse
        // (the dispatcher uses the same warn-and-delete policy, service.rs:419-426)
        tracing::warn!(%serde_err, "dropping unreadable queue rows");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling list(), list_page(), update(), or start() while the store contains a payload that fails to deserialize as TurnInput; version skew between the writer and reader of the queue table; hand-edited or migrated queue rows.

Common situations: Upgrading codex while old queued messages exist and the TurnInput shape changed; two different versions running against the same state database; test fixtures with hand-written payloads.

Related errors


AI-assisted analysis of openai/codex@339751715c (2026-08-25). Data as JSON: /api/errors/402d2ce90f7914b9. Report an issue: GitHub.