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
- Check the embedded io::Error kind; NotFound and PermissionDenied name the failing attachment path.
- Verify each attachment path exists and is readable before calling enqueue().
- Re-attach the file from its current location if it moved.
- 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
- Keep attachment files alive until enqueue completes; do not clean temp dirs mid-send.
- Validate paths exist and are readable before queueing.
- Check the embedded io::Error kind; it identifies the failing file.
- Prefer durable locations over temp paths for attachments that may wait in the queue.
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
- failed to read app config for selected plugin `{plugin_id}`
- failed to read MCP config for selected plugin `{plugin_id}`
- I/O error while reading memories: {0}
- queue storage failed: {0}
- queued submission payload is invalid: {0}
AI-assisted analysis of openai/codex@339751715c (2026-08-25).
Data as JSON: /api/errors/626ebc13fbeac592.
Report an issue: GitHub.