{"record":{"id":"a2f0c89cf67621eb","repo":"openai/codex","slug":"queue-storage-failed-0","errorCode":null,"errorMessage":"queue storage failed: {0}","messagePattern":"queue storage failed: (.+?)","errorType":"exception","errorClass":"QueueServiceError","httpStatus":null,"severity":"error","filePath":"codex-rs/ext/queue/src/service.rs","lineNumber":48,"sourceCode":"use codex_thread_store::QueueStore;\nuse codex_thread_store::QueuedUserSubmissionRecord;\nuse codex_thread_store::ThreadStoreError;\nuse thiserror::Error;\nuse tokio::sync::Mutex;\nuse tokio::sync::OwnedMutexGuard;\nuse 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>,","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/ext/queue/src/service.rs#L30-L66","documentation":"Wraps ThreadStoreError via #[from] (codex-rs/ext/queue/src/service.rs:48-49) and is returned by every QueuedItemService method that touches the underlying QueueStore: enqueue, list/list_page, update, delete, reorder, and start. The local store is SQLite-backed and converts storage-layer failures into ThreadStoreError::Internal (queue_store.rs:78-81). start() also fabricates ThreadStoreError::InvalidRequest ('queue is empty' / 'queued submission not found: {id}') when the requested item is missing (service.rs:380-385), and reorder() returns InvalidRequest when the ids are not an exact permutation of the queue.","triggerScenarios":"Any queue operation hitting a SQLite error (locked database, corrupt file, disk full); reorder() with an id list that omits or duplicates queue items; start(thread, Some(id)) where the item was already dequeued by another client.","commonSituations":"Two processes sharing one state database (lock contention); a UI acting on a stale queue snapshot after another client consumed an item; drag-and-drop reorder that dropped an item from the list.","solutions":["Read the inner ThreadStoreError: InvalidRequest means bad request data (fix the ids), Internal usually means SQLite-level trouble.","For 'queued submission not found' or an empty queue, re-fetch with list(thread_id) and act on current ids.","For reorder, pass exactly the ids returned by list(), each exactly once, in the new order.","For Internal SQLite errors, check the state database's permissions, concurrent holders, and disk space."],"exampleFix":"// before\nservice.reorder(thread_id, vec![first_id]).await?; // partial list => InvalidRequest\n\n// after - ids must be the complete queue, each once, in the new order\nlet ids: Vec<String> = desired_order.into_iter().map(|item| item.id).collect();\nservice.reorder(thread_id, ids).await?;","handlingStrategy":"try-catch","validationCode":"// Rust - verify a reorder payload is a permutation of the live queue before calling reorder\nlet current: std::collections::HashSet<String> =\n    service.list(thread_id).await?.into_iter().map(|i| i.id).collect();\nlet next: std::collections::HashSet<String> = new_order.iter().cloned().collect();\ndebug_assert_eq!(current, next); // caller guarantee: every id, exactly once\nservice.reorder(thread_id, new_order).await","typeGuard":null,"tryCatchPattern":"match service.start(thread, Some(id), None).await {\n    Ok(submission) => { /* ... */ }\n    Err(QueueServiceError::Storage(ThreadStoreError::InvalidRequest { message }))\n        if message.contains(\"not found\") || message.contains(\"empty\") =>\n    {\n        // stale snapshot: refresh with list() and pick a current id\n    }\n    Err(QueueServiceError::Storage(ThreadStoreError::Internal { message })) => {\n        // SQLite-level failure: check db locks, permissions, disk space\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Re-fetch the queue with list() before acting on ids captured earlier.","reorder() takes the complete id set exactly once; derive it from list().","Avoid concurrent writers on one state database; SQLite lock errors surface as Internal.","Handle InvalidRequest separately from Internal - only the first is a caller bug."],"tags":["rust","codex","queue","storage","sqlite","thread-store"],"backgroundTag":"database-operation-failed","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}