{"record":{"id":"6e727a50e86f1fde","repo":"gitbutlerapp/gitbutler","slug":"projectmissing","errorCode":"ProjectMissing","errorMessage":"Could not open repository at '{}'{suffix}","messagePattern":"Could not open repository at '(.+?)'(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/gitbutler-project/src/controller.rs","lineNumber":299,"sourceCode":"    /// Like [`Self::get()`], but will assure the project still exists and is valid by\n    /// opening a git repository. This should only be done for critical points in time.\n    pub(crate) fn get_validated(&self, id: ProjectHandleOrLegacyProjectId) -> Result<Project> {\n        self.get_inner(id, true)\n    }\n\n    fn get_inner(&self, id: ProjectHandleOrLegacyProjectId, validate: bool) -> Result<Project> {\n        let mut project = self.projects_storage.get(id)?;\n        // BACKWARD-COMPATIBLE MIGRATION\n        project.migrate()?;\n        if validate {\n            let repo = project.open_isolated_repo();\n            if repo.is_err() {\n                let suffix = if !project.worktree_dir.exists() {\n                    \" as it does not exist\"\n                } else {\n                    \"\"\n                };\n                return Err(anyhow!(\n                    \"Could not open repository at '{}'{suffix}\",\n                    project.worktree_dir.display()\n                )\n                .context(Code::ProjectMissing));\n            }\n        }\n\n        match project.gb_dir() {\n            Ok(gb_dir) => {\n                if !gb_dir.exists()\n                    && let Err(error) = std::fs::create_dir_all(&gb_dir)\n                {\n                    tracing::error!(project_id = %project.id, ?error, \"failed to create \\\"{}\\\" on project get\", gb_dir.display());\n                }\n            }\n            Err(error) => {\n                tracing::error!(project_id = %project.id, ?error, \"failed to resolve storage directory on project get\");\n            }","sourceCodeStart":281,"sourceCodeEnd":317,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/gitbutler-project/src/controller.rs#L281-L317","documentation":"ProjectController::get_inner with validate=true opens an isolated repo at project.worktree_dir; if open_isolated_repo fails, the error 'Could not open repository at ...' is returned with Code::ProjectMissing. The suffix ' as it does not exist' is appended when the worktree directory is missing entirely, distinguishing a vanished path from an unopenable (present but broken) repository.","triggerScenarios":"The project folder was moved, renamed, or deleted after being added; an external/unmounted drive holding the worktree; a corrupted .git making even isolated open fail; a stale project row in the app database pointing at a path from another machine.","commonSituations":"Users moving repositories in Finder/Explorer after onboarding; projects on network mounts or removable media that are disconnected at startup; synced settings carrying project records to a machine where the path does not exist; .git dir permissions broken.","solutions":["Confirm the path still exists and contains a valid .git; if it moved, re-add the project at the new location and remove the stale entry","Reconnect the drive/mount that hosts the worktree, then retry","On startup, drop project records whose worktree_dir no longer exists so users re-onboard cleanly instead of hitting ProjectMissing mid-session","If the directory exists but still fails, inspect .git integrity (git fsck) — a present-but-broken repo produces the suffix-less variant"],"exampleFix":"// before\nlet project = controller.get_inner(id_or_handle, /* validate */ true)?;\n\n// after: pre-validate stored projects and evict stale ones\nlet project = controller.get_inner(id_or_handle, false)?;\nif !project.worktree_dir.is_dir() {\n    controller.delete_project(project.id)?;\n    anyhow::bail!(\"project path {} is gone — re-add the project\", project.worktree_dir.display());\n}\nlet project = controller.get_inner(id_or_handle, true)?;","handlingStrategy":"validation","validationCode":"// validate the stored path before a validating get\nlet stored = controller.get_inner(id_or_handle.clone(), /* validate */ false)?;\nif !stored.worktree_dir.exists() {\n    anyhow::bail!(\n        \"project path {} no longer exists — remove the project and re-add it\",\n        stored.worktree_dir.display()\n    );\n}\nlet project = controller.get_inner(id_or_handle, true)?;","typeGuard":"fn project_is_on_disk(project: &Project) -> bool {\n    project.worktree_dir.is_dir()\n}","tryCatchPattern":"match controller.get_inner(id, true) {\n    Ok(project) => Ok(project),\n    Err(err) if err.to_string().contains(\"Could not open repository\") => {\n        // Code::ProjectMissing: drive unmounted / folder moved — prompt re-add\n        prompt_user_to_re_add_project()\n    }\n    Err(err) => Err(err),\n}","preventionTips":["Validate project paths at startup and offer re-onboarding for missing directories","Detect moved repositories (search nearby paths for the same repo id) before failing","For removable-media projects, check mount state before opening and surface a 'drive not connected' state instead of an error"],"tags":["gitbutler","project","filesystem","startup"],"backgroundTag":"project-directory-missing","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-23T16:17:53.355Z"}