{"record":{"id":"5d0d286ceefaef00","repo":"jdx/mise","slug":"relative-repo-paths-are-only-allowed-in-a-project","errorCode":null,"errorMessage":"relative repo paths are only allowed in a project config; use an absolute path or a `~/` path","messagePattern":"relative repo paths are only allowed in a project config; use an absolute path or a `~/` path","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/repos.rs","lineNumber":90,"sourceCode":"}\n\nimpl RepoRequest {\n    pub fn from_toml(\n        path_raw: String,\n        config: RepoTomlConfig,\n        project_root: Option<&Path>,\n    ) -> Result<Self> {\n        if path_raw.starts_with('~') && !path_raw.starts_with(\"~/\") {\n            bail!(\n                \"repo path `{path_raw}` cannot start with `~`; use `~/` for a home-relative path\"\n            );\n        }\n        let path = file::replace_path(&path_raw);\n        let path = if path.is_absolute() {\n            path\n        } else {\n            let Some(root) = project_root else {\n                bail!(\n                    \"relative repo paths are only allowed in a project config; use an absolute path or a `~/` path\"\n                );\n            };\n            if path.components().any(|component| {\n                matches!(\n                    component,\n                    Component::ParentDir | Component::RootDir | Component::Prefix(_)\n                )\n            }) {\n                bail!(\n                    \"relative repo path `{path_raw}` must stay within the project root (no `..` or absolute segments)\"\n                );\n            }\n            if !path\n                .components()\n                .any(|component| matches!(component, Component::Normal(_)))\n            {\n                bail!(","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/repos.rs#L72-L108","documentation":"Thrown by RepoRequest::from_toml while parsing an `[bootstrap.repos]` entry in mise.toml. A repo path key that is relative (not starting with `/` or `~/`) can only be resolved against a project root — the directory of the config file being read. When from_toml is called with project_root=None, which happens for the global config (~/.config/mise/mise.toml) or a standalone config file, no base directory exists, so the entry is rejected instead of silently resolving against the current working directory.","triggerScenarios":"Declaring `[bootstrap.repos]` with a key like `vendor/tool` in the global config (~/.config/mise/mise.toml) or any config parsed without a project root; any key that is not absolute and does not start with `~/`.","commonSituations":"Copying a project-level mise.toml snippet into the global config; declaring repos in a standalone config file (MISE_CONFIG_FILE) with no project directory; migrating a config from a repo into a dotfiles setup.","solutions":["Rewrite the repo key with a `~/` prefix (e.g. `\"~/src/vendor/tool\"`) for global configs","Move the `[bootstrap.repos]` table into the project's mise.toml so the relative path resolves against the project root","Use a fully absolute path such as `/opt/repos/tool`"],"exampleFix":"# before — ~/.config/mise/mise.toml (global config)\n[bootstrap.repos]\n\"vendor/tool\" = { url = \"https://example.com/tool.git\" }\n\n# after\n[bootstrap.repos]\n\"~/src/vendor/tool\" = { url = \"https://example.com/tool.git\" }","handlingStrategy":"validation","validationCode":"fn repo_path_works_without_root(path_raw: &str) -> bool {\n    let p = std::path::Path::new(path_raw);\n    p.is_absolute() || path_raw.starts_with(\"~/\")\n}\n\n// before building requests from a rootless config:\nfor path_raw in config_repos.keys() {\n    if project_root.is_none() && !repo_path_works_without_root(path_raw) {\n        // would bail: rewrite to ~/… or absolute, or parse this config with a project root\n    }\n}","typeGuard":"fn is_rootless_repo_path(path_raw: &str) -> bool {\n    std::path::Path::new(path_raw).is_absolute() || path_raw.starts_with(\"~/\")\n}","tryCatchPattern":null,"preventionTips":["Keep `[bootstrap.repos]` entries with relative keys in the project mise.toml only","Prefer `~/`-prefixed paths in global config so they resolve regardless of cwd","Run `mise bootstrap --dry-run` after moving config files to catch path validation early"],"tags":["mise","bootstrap","repos","config","path-validation","global-config"],"backgroundTag":"relative-path-without-base-dir","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}