{"record":{"id":"0f366a3c20ca277c","repo":"gitbutlerapp/gitbutler","slug":"configured-storage-path-resolves-to-git-it","errorCode":null,"errorMessage":"configured storage path '{}' resolves to '.git' itself; choose a dedicated GitButler directory instead","messagePattern":"configured storage path '(.+?)' resolves to '\\.git' itself; choose a dedicated GitButler directory instead","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-project-handle/src/storage_path.rs","lineNumber":91,"sourceCode":"    }\n\n    let gitdir_real = gix::path::realpath(git_dir)?;\n    Ok(storage_path\n        .strip_prefix(&gitdir_real)\n        .ok()\n        .map(Path::to_owned))\n}\n\n/// Only accept `storage_path_relative_to_git_dir` if it's a top-level `gitbutler` directory.\n/// Use `storage_path` to format error messages.\nfn validate_in_git_storage_path(\n    storage_path_relative_to_git_dir: &Path,\n    storage_path: &Path,\n) -> anyhow::Result<()> {\n    let Some(Component::Normal(top_level_dir)) =\n        storage_path_relative_to_git_dir.components().next()\n    else {\n        bail!(\n            \"configured storage path '{}' resolves to '.git' itself; choose a dedicated GitButler directory instead\",\n            storage_path.display()\n        );\n    };\n\n    if !top_level_dir\n        .to_string_lossy()\n        .get(..DEFAULT_STORAGE_DIR_NAME.len())\n        .is_some_and(|name| name.eq_ignore_ascii_case(DEFAULT_STORAGE_DIR_NAME))\n    {\n        bail!(\n            \"configured storage path '{}' resolves inside '.git' but not under a top-level 'gitbutler*' directory\",\n            storage_path.display()\n        );\n    }\n\n    Ok(())\n}","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-project-handle/src/storage_path.rs#L73-L109","documentation":"GitButler validates a configured in-git storage path (`storage_path_relative_to_git_dir`): its first component must be a normal directory name so storage resolves under `.git/<dir>`, never to `.git` itself. An empty or '.'-equivalent value would place the database and sentinel files directly inside the git dir and is refused.","triggerScenarios":"Setting the storage path override to '.', an empty value, or any path whose first component is not a normal directory, so the resolved directory is the .git directory itself.","commonSituations":"Users trying to keep everything directly in .git by configuring '.'; a settings UI writing an empty string instead of unsetting the override; migrations that preserved an empty value.","solutions":["Set the override to a dedicated top-level directory under .git, e.g. 'gitbutler'","Remove the custom storage-path override entirely to fall back to the default 'gitbutler' directory","If a custom name is wanted, use any top-level name starting with 'gitbutler' (case-insensitive)"],"exampleFix":"# before\nstorage_path_relative_to_git_dir = \"\"\n\n# after\nstorage_path_relative_to_git_dir = \"gitbutler\"","handlingStrategy":"validation","validationCode":"// Rust: validate the in-git storage path before saving the setting\nuse std::path::Component;\nfn starts_with_normal_dir(rel: &std::path::Path) -> bool {\n    matches!(rel.components().next(), Some(Component::Normal(_)))\n}\nif !starts_with_normal_dir(configured_rel) {\n    return Err(anyhow::anyhow!(\"storage path must be a directory under .git, not .git itself\"));\n}","typeGuard":null,"tryCatchPattern":"Catch the bail from settings save, show the configured value, and offer to reset the override to the default 'gitbutler' directory.","preventionTips":["Never set the override to '.', empty, or root; treat empty input in settings as 'unset'","Validate the value in the settings UI before persisting it"],"tags":["config","storage-path","git-dir","validation","gitbutler"],"backgroundTag":"invalid-config-path","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}