jdx/mise · error

relative repo path `{path_raw}` must stay within the project

Error message

relative repo path `{path_raw}` must stay within the project root (no `..` or absolute segments)

What it means

After a project root is found, each component of the relative repo path is inspected; any `..` (ParentDir), root `/` (RootDir), or Windows drive-prefix (Prefix) component means the path could point outside the project root, and from_toml rejects it. This is a containment guard: with a relative key, bootstrap only manages checkouts inside the project. Repos elsewhere must use `~/` or absolute paths.

Source

Thrown at src/system/repos.rs:100

                "repo path `{path_raw}` cannot start with `~`; use `~/` for a home-relative path"
            );
        }
        let path = file::replace_path(&path_raw);
        let path = if path.is_absolute() {
            path
        } else {
            let Some(root) = project_root else {
                bail!(
                    "relative repo paths are only allowed in a project config; use an absolute path or a `~/` path"
                );
            };
            if path.components().any(|component| {
                matches!(
                    component,
                    Component::ParentDir | Component::RootDir | Component::Prefix(_)
                )
            }) {
                bail!(
                    "relative repo path `{path_raw}` must stay within the project root (no `..` or absolute segments)"
                );
            }
            if !path
                .components()
                .any(|component| matches!(component, Component::Normal(_)))
            {
                bail!(
                    "relative repo path `{path_raw}` must name a directory inside the project root"
                );
            }
            // Join only the Normal segments so `./foobar` resolves to
            // `<root>/foobar` rather than `<root>/./foobar` — a `.` component
            // survives `Path::join` and leaks into every displayed path.
            let mut resolved = root.to_path_buf();
            for component in path.components() {
                if let Component::Normal(segment) = component {
                    resolved.push(segment);

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Point the key inside the project (drop the `..` segments)
  2. Use a `~/` path such as `"~/src/shared"` for repos that live outside the project
  3. Use an absolute path for machine-specific locations

Example fix

# before
[bootstrap.repos]
"../shared" = { url = "git@github.com:team/shared.git" }

# after
[bootstrap.repos]
"~/src/shared" = { url = "git@github.com:team/shared.git" }
Defensive patterns

Strategy: validation

Validate before calling

use std::path::{Component, Path};

fn repo_path_stays_in_root(path_raw: &str) -> bool {
    !Path::new(path_raw).components().any(|c| {
        matches!(c, Component::ParentDir | Component::RootDir | Component::Prefix(_))
    })
}

Type guard

fn is_contained_relative_repo_path(path_raw: &str) -> bool {
    !std::path::Path::new(path_raw)
        .components()
        .any(|c| matches!(c, std::path::Component::ParentDir | std::path::Component::RootDir | std::path::Component::Prefix(_)))
}

Prevention

When it happens

Trigger: Keys like `"../shared"`, `"vendor/../../escape"`, or (on Windows) a key embedding a `C:\` prefix inside an otherwise relative path, in a project mise.toml.

Common situations: Repos intentionally stored as siblings of the project (`../lib/foo`); refactoring from absolute to relative paths; monorepo layouts where the checkout lives one level above the config.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/738ec9c87cdcf9c1. Report an issue: GitHub.