openai/codex · error

{kind} input exceeds {max_bytes} bytes

Error message

{kind} input exceeds {max_bytes} bytes

What it means

io::Error(ErrorKind::InvalidData, '{kind} input exceeds {max_bytes} bytes') raised by read_bounded_local_media (codex-rs/protocol/src/local_media.rs:56-61) during the pre-read metadata check: file.metadata().len() is compared to the cap before any bytes are read. kind is 'image' with the image byte cap from codex_utils_image (MAX_PROMPT_IMAGE_INPUT_BYTES) or 'audio' with MAX_PROMPT_AUDIO_INPUT_BYTES = 50 MiB (local_media.rs:16). Oversized local media is refused so it is never read into memory or uploaded.

Source

Thrown at codex-rs/protocol/src/local_media.rs:57

            })?;
            let file_bytes = read_bounded_local_media(path, MAX_PROMPT_AUDIO_INPUT_BYTES, "audio")?;
            *input = UserInput::Audio {
                audio_url: data_url_from_bytes(mime, &file_bytes),
            };
        }
        UserInput::Text { .. }
        | UserInput::Image { .. }
        | UserInput::Audio { .. }
        | UserInput::Skill { .. }
        | UserInput::Mention { .. } => {}
    }
    Ok(())
}

fn read_bounded_local_media(path: &Path, max_bytes: usize, kind: &str) -> io::Result<Vec<u8>> {
    let file = std::fs::File::open(path)?;
    if file.metadata()?.len() > max_bytes as u64 {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!("{kind} input exceeds {max_bytes} bytes"),
        ));
    }

    let mut bytes = Vec::new();
    file.take(max_bytes as u64 + 1).read_to_end(&mut bytes)?;
    if bytes.len() > max_bytes {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!("{kind} input exceeds {max_bytes} bytes"),
        ));
    }
    Ok(bytes)
}

pub(crate) fn audio_mime_for_path(path: &Path) -> Option<&'static str> {
    let extension = path.extension()?.to_str()?;

View on GitHub (pinned to 339751715c)

Solutions

  1. Shrink the file below the printed cap: compress or resize images (avoid forcing ImageDetail::Original so Codex can resize), transcode audio to MP3/OGG or trim it.
  2. Pre-check size before queueing: compare fs::metadata(path).len() against the same limits.
  3. For long audio, use compressed containers - they fit far more duration per byte than WAV.
  4. If all the content matters, split it into several smaller inputs.

Example fix

// before: queueing a giant WAV
UserInput::LocalAudio { path: Path::new("/tmp/3h.wav").into() }
// > MAX_PROMPT_AUDIO_INPUT_BYTES (50 MiB) -> InvalidData

// after: transcode/trim under the cap first
// ffmpeg -i 3h.wav -t 1800 -b:a 64k excerpt.mp3
UserInput::LocalAudio { path: Path::new("/tmp/excerpt.mp3").into() }
Defensive patterns

Strategy: validation

Validate before calling

fn within_cap(path: &Path, max_bytes: usize) -> bool {
    std::fs::metadata(path)
        .map(|m| m.len() <= max_bytes as u64)
        .unwrap_or(false)
}
// images: max_bytes = codex_utils_image::MAX_PROMPT_IMAGE_INPUT_BYTES
// audio:  max_bytes = MAX_PROMPT_AUDIO_INPUT_BYTES (50 MiB)

Type guard

fn is_size_exceeded(err: &io::Error) -> bool {
    err.kind() == io::ErrorKind::InvalidData && err.to_string().contains("input exceeds")
}

Try / catch

Err(e) if e.kind() == io::ErrorKind::InvalidData && e.to_string().contains("input exceeds") => {
    // surface the cap from the message; ask the user to compress or trim
}

Prevention

When it happens

Trigger: snapshot_local_user_input on a UserInput::LocalImage larger than the image byte cap, or a UserInput::LocalAudio larger than 50 MiB; the message states which kind and the exact byte limit.

Common situations: Huge screenshots or uncompressed TIFF/RAW photos as images; hour-long uncompressed WAV recordings (50 MiB is roughly 50 minutes of WAV); pipeline artifacts that were never compressed.

Related errors


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