rust-lang/cargo · error

can only edit absolute paths, got

Error message

can only edit absolute paths, got {}

What it means

Thrown by LocalManifest::try_new when the provided path is not absolute. The editor needs an absolute path because it reads the file, may resolve includes/paths relative to it, and writes back to the same location; relative paths would make the operation dependent on the process's current directory, which is fragile. It uses Path::is_absolute to check.

Solutions

  1. Canonicalize the path before calling try_new: std::fs::canonicalize(&path) or std::env::current_dir().join(path).
  2. If the path comes from CLI args, use std::env::current_dir().join(arg) to absolutize it.
  3. If the path may not exist yet (so canonicalize fails), join it with current_dir() manually.

Example fix

// before
let manifest = LocalManifest::try_new(Path::new("Cargo.toml"))?; // errors

// after
let abs = std::env::current_dir()?.join("Cargo.toml");
let manifest = LocalManifest::try_new(&abs)?;
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_absolute(path: &Path) -> CargoResult<PathBuf> {
    if path.is_absolute() { Ok(path.to_path_buf()) }
    else { Ok(std::env::current_dir()?.join(path)) }
}
// let abs = ensure_absolute(&path)?;
// let manifest = LocalManifest::try_new(&abs)?;

Type guard

fn is_absolute_path(p: &Path) -> bool { p.is_absolute() }

Prevention

When it happens

Trigger: Calling LocalManifest::try_new(&Path::new("Cargo.toml")) or any relative path. Common when a caller obtains a path from user input or from a config file without canonicalizing it first.

Common situations: A CLI tool passes a user-supplied relative path directly. A test uses a relative fixture path. Joining a relative base with a filename but forgetting to canonicalize. Paths read from environment variables that are relative.

Related errors


AI-assisted analysis of rust-lang/cargo@eb98b54bc9 (2026-08-11). Data as JSON: /api/errors/a848e905967ee656. Report an issue: GitHub.

Appendix: source

Thrown at src/workspace/editor/manifest.rs:268

impl Deref for LocalManifest {
    type Target = Manifest;

    fn deref(&self) -> &Manifest {
        &self.manifest
    }
}

impl DerefMut for LocalManifest {
    fn deref_mut(&mut self) -> &mut Manifest {
        &mut self.manifest
    }
}

impl LocalManifest {
    /// Construct the `LocalManifest` corresponding to the `Path` provided..
    pub fn try_new(path: &Path) -> CargoResult<Self> {
        if !path.is_absolute() {
            anyhow::bail!("can only edit absolute paths, got {}", path.display());
        }
        let raw = cargo_util::paths::read(&path)?;
        let mut data = raw.clone();
        let mut embedded = None;
        if is_embedded(path) {
            let source = ScriptSource::parse(&data)?;
            if let Some(frontmatter) = source.frontmatter_span() {
                embedded = Some(Embedded::exists(frontmatter));
                data = source.frontmatter().unwrap().to_owned();
            } else if let Some(shebang) = source.shebang_span() {
                embedded = Some(Embedded::after(shebang));
                data = String::new();
            } else {
                embedded = Some(Embedded::start());
                data = String::new();
            }
        }
        let manifest = data.parse().context("unable to parse Cargo.toml")?;

View on GitHub (pinned to eb98b54bc9)