{"record":{"id":"ecdafba77751450d","repo":"jdx/mise","slug":"relative-repo-path-path-raw-must-name-a-direct","errorCode":null,"errorMessage":"relative repo path `{path_raw}` must name a directory inside the project root","messagePattern":"relative repo path `(.+?)` must name a directory inside the project root","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/repos.rs","lineNumber":108,"sourceCode":"                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!(\n                    \"relative repo path `{path_raw}` must name a directory inside the project root\"\n                );\n            }\n            // Join only the Normal segments so `./foobar` resolves to\n            // `<root>/foobar` rather than `<root>/./foobar` — a `.` component\n            // survives `Path::join` and leaks into every displayed path.\n            let mut resolved = root.to_path_buf();\n            for component in path.components() {\n                if let Component::Normal(segment) = component {\n                    resolved.push(segment);\n                }\n            }\n            resolved\n        };\n        let Some(url) = config.url.map(|s| s.trim().to_string()) else {\n            bail!(\"must set `url`\");\n        };\n        if url.is_empty() {","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/repos.rs#L90-L126","documentation":"A relative repo path that contains no Normal component — the empty string `\"\"` or `\".\"` — would resolve to the project root itself; from_toml rejects it because bootstrap would otherwise try to manage the whole project directory as a repo checkout. Any path naming at least one real subdirectory passes (leading `./` is fine and is normalized away).","triggerScenarios":"An `[bootstrap.repos]` key of `\"\"`, `\".\"`, or a key made only of `./` segments in a project config.","commonSituations":"Placeholder keys left from templating; attempting to pin the current project as its own bootstrap repo; TOML copy-paste where the path was never filled in.","solutions":["Name an actual subdirectory, e.g. `\"vendor/tool\"`","If you meant the project currently being configured, remove the entry — a project does not declare itself as a bootstrap repo"],"exampleFix":"# before\n[bootstrap.repos]\n\".\" = { url = \"https://example.com/tool.git\" }\n\n# after\n[bootstrap.repos]\n\"vendor/tool\" = { url = \"https://example.com/tool.git\" }","handlingStrategy":"validation","validationCode":"use std::path::{Component, Path};\n\nfn repo_path_names_a_directory(path_raw: &str) -> bool {\n    Path::new(path_raw)\n        .components()\n        .any(|c| matches!(c, Component::Normal(_)))\n}","typeGuard":"fn names_subdirectory(path_raw: &str) -> bool {\n    std::path::Path::new(path_raw)\n        .components()\n        .any(|c| matches!(c, std::path::Component::Normal(_)))\n}","tryCatchPattern":null,"preventionTips":["Treat `\"\"` and `\".\"` repo keys as config bugs — lint for them in CI","Fill in placeholder keys before committing templated mise.toml files"],"tags":["mise","bootstrap","repos","path-validation","config"],"backgroundTag":"empty-path-in-config","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}