openai/codex · error · QueueServiceError

local queued attachment is invalid: {0}

Error message

local queued attachment is invalid: {0}

What it means

Wraps std::io::Error raised while preparing a queued message for durable storage (codex-rs/ext/queue/src/service.rs:52-53). prepare_queued_user_input (service.rs:484-523) runs snapshot_local_user_input on every LocalImage/LocalAudio item inside spawn_blocking; if any attachment cannot be read or snapshotted, the io::Error converts into this variant, and a panicked blocking task also lands here as io::Error::other. The failure happens before the item is enqueued, so nothing is persisted.

Source

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

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>,
    dispatch_locks: Arc<StdMutex<HashMap<ThreadId, Weak<Mutex<()>>>>>,
    resumed_threads: Arc<StdMutex<HashSet<ThreadId>>>,

View on GitHub (pinned to 339751715c)

Solutions

  1. Check the embedded io::Error kind; NotFound and PermissionDenied name the failing attachment path.
  2. Verify each attachment path exists and is readable before calling enqueue().
  3. Re-attach the file from its current location if it moved.
  4. Keep attachment sources alive (do not clean temp files) until the enqueue future completes.

Example fix

// before
service.enqueue(thread_id, input_with_local_image).await?; // InvalidAttachment if the file vanished

// after
if let TurnInput::UserInput { content, .. } = &input_with_local_image {
    for item in content {
        let path = match item {
            UserInput::LocalImage { path, .. } | UserInput::LocalAudio { path } => path,
            _ => continue,
        };
        tokio::fs::metadata(path).await?; // fail fast with a clear path
    }
}
service.enqueue(thread_id, input_with_local_image).await?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust - verify local attachments before enqueue
async fn attachments_ok(content: &[UserInput]) -> std::io::Result<()> {
    for item in content {
        let path = match item {
            UserInput::LocalImage { path, .. } | UserInput::LocalAudio { path } => path,
            _ => continue,
        };
        tokio::fs::metadata(path).await?; // surfaces NotFound/PermissionDenied early
    }
    Ok(())
}

Type guard

// Rust - does this input need attachment verification before queueing?
fn has_local_attachments(content: &[UserInput]) -> bool {
    content.iter().any(|item| matches!(
        item,
        UserInput::LocalImage { .. } | UserInput::LocalAudio { .. }
    ))
}

Prevention

When it happens

Trigger: enqueue() or update() with a TurnInput containing LocalImage/LocalAudio whose path no longer exists or is unreadable; an attachment written to a temp directory that has since been cleaned up; permission changes between attach time and queue time.

Common situations: Temp-file cleanup races (the file is deleted between selection and send); paths captured on another machine or container; attachments on removable or permission-restricted storage.

Related errors


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