openai/codex · error · std::io::Error

InvalidInput

InvalidInput

Error message

path is a directory

What it means

ensure_not_directory fetches metadata for a target path (honoring the follow_symlinks option) and returns io::ErrorKind::InvalidInput with 'path is a directory' when the path resolves to a directory. apply-patch invokes it before file writes, so a patch that tries to add or update a 'file' whose path is an existing directory fails fast instead of corrupting the directory. It propagates wrapped in ApplyPatchFailure together with the delta of changes already committed.

Source

Thrown at codex-rs/apply-patch/src/lib.rs:738

    }
    Ok(AffectedPaths {
        added,
        modified,
        deleted,
    })
}

async fn ensure_not_directory(
    path: &PathUri,
    fs: &dyn ExecutorFileSystem,
    follow_symlinks: bool,
    sandbox: Option<&FileSystemSandboxContext>,
) -> io::Result<()> {
    let metadata = fs
        .get_metadata(path, GetMetadataOptions { follow_symlinks }, sandbox)
        .await?;
    if metadata.is_directory {
        return Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            "path is a directory",
        ));
    }
    Ok(())
}

async fn remove_failure_was_side_effect_free(
    path: &PathUri,
    expected_content: Option<&str>,
    fs: &dyn ExecutorFileSystem,
    follow_symlinks: bool,
    sandbox: Option<&FileSystemSandboxContext>,
) -> bool {
    match expected_content {
        Some(expected_content) => fs
            .read_file_text(path, ReadFileOptions { follow_symlinks }, sandbox)
            .await

View on GitHub (pinned to 339751715c)

Solutions

  1. Look at the failing path named in the error context and change the patch so it targets a file path that does not collide with an existing directory
  2. If the directory is stale or wrong, remove or rename it, then re-apply the patch
  3. For '*** Move to:' hunks, make sure the destination is a file path, not a directory
  4. Check symlinks on the path: with follow_symlinks enabled a link can resolve to a directory

Example fix

# before (patch hunk targets a directory)
*** Update File: src/utils

# after
*** Update File: src/utils.rs
Defensive patterns

Strategy: validation

Validate before calling

for hunk in hunks {
    let p = cwd.as_path().join(hunk.path());
    if let Ok(meta) = std::fs::metadata(&p) {
        if meta.is_dir() {
            return Err(format!("patch target is a directory: {}", p.display()));
        }
    }
}

Try / catch

if let Err(failure) = apply_hunks(&hunks, &cwd, &mut out, &mut err, fs, None).await {
    let (err, _) = failure.into_parts();
    if let ApplyPatchError::IoError(io) = err {
        if io.source().kind() == std::io::ErrorKind::InvalidInput { /* directory target */ }
    }
}

Prevention

When it happens

Trigger: An '*** Add File:' or '*** Update File:' hunk whose path names an existing directory; a '*** Move to:' destination that is a directory; a symlink that resolves (follow_symlinks=true) to a directory.

Common situations: A model-generated patch uses a path that collides with an existing directory (e.g. 'src/utils' where utils is a directory); the repo layout changed between patch generation and application; path-join or trailing-slash bugs producing a directory path.

Related errors


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