{"record":{"id":"2fbf0c76a82055f8","repo":"zed-industries/zed","slug":"grammar-directory-already-exists-but-is-not","errorCode":null,"errorMessage":"grammar directory '{}' already exists, but is not a git clone of '{}'","messagePattern":"grammar directory '(.+?)' already exists, but is not a git clone of '(.+?)'","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/extension/src/extension_builder.rs","lineNumber":376,"sourceCode":"\n        Ok(())\n    }\n\n    async fn checkout_repo(&self, directory: &Path, url: &str, rev: &str) -> Result<()> {\n        let git_dir = directory.join(\".git\");\n\n        if directory.exists() {\n            let remotes_output = util::command::new_command(\"git\")\n                .arg(\"--git-dir\")\n                .arg(&git_dir)\n                .args([\"remote\", \"get-url\", \"origin\"])\n                .env(\"GIT_CONFIG_GLOBAL\", \"/dev/null\")\n                .output()\n                .await?;\n            let has_remote = remotes_output.status.success()\n                && String::from_utf8_lossy(&remotes_output.stdout).trim() == url;\n            if !has_remote {\n                bail!(\n                    \"grammar directory '{}' already exists, but is not a git clone of '{}'\",\n                    directory.display(),\n                    url\n                );\n            }\n        } else {\n            fs::create_dir_all(directory).with_context(|| {\n                format!(\"failed to create grammar directory {}\", directory.display(),)\n            })?;\n            let init_output = util::command::new_command(\"git\")\n                .arg(\"init\")\n                .current_dir(directory)\n                .output()\n                .await?;\n            if !init_output.status.success() {\n                bail!(\n                    \"failed to run `git init` in directory '{}'\",\n                    directory.display()","sourceCodeStart":358,"sourceCodeEnd":394,"githubUrl":"https://github.com/zed-industries/zed/blob/f4178619acd0d47ea1f76a2025c42962c6d6638c/crates/extension/src/extension_builder.rs#L358-L394","documentation":"checkout_repo() reuses a cached grammar directory between builds. If the directory exists, it runs 'git --git-dir <dir>/.git remote get-url origin' (with GIT_CONFIG_GLOBAL=/dev/null) and requires the output to equal the manifest's git URL. A mismatch - or the git command failing because the directory is not a git clone at all - bails with this error, refusing to reuse the cache for a different repository.","triggerScenarios":"Building an extension whose grammar cache directory exists but its origin remote points elsewhere: the extension.toml grammar 'git' URL was changed between builds (repo moved, fork switched), the directory was created by something other than git, or .git is corrupt so 'remote get-url' exits non-zero (has_remote becomes false).","commonSituations":"Extension author renames the upstream repo or swaps a grammar for a fork; a previous build crashed midway leaving a non-git directory; the cache dir was manually populated or restored from a bad backup.","solutions":["Delete the offending cached grammar directory under the extension builder's cache (the 'grammars' cache next to its work/cache dirs, e.g. ~/.cache/zed/extensions/...) so the next build re-clones from the new URL","If the URL change was unintentional, revert extension.toml to the URL the cache was cloned from","Pre-check which URL the cache holds: git --git-dir <cache>/.git remote get-url origin, and keep it in sync whenever you edit the grammar URL"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Pre-check the cache before building\nlet expected_url = &grammar_meta.git;\nlet output = std::process::Command::new(\"git\")\n    .args([\"--git-dir\", cache_grammar_git_dir.to_str().unwrap(), \"remote\", \"get-url\", \"origin\"])\n    .output()?;\nlet cache_matches = output.status.success()\n    && String::from_utf8_lossy(&output.stdout).trim() == expected_url;\nif !cache_matches {\n    std::fs::remove_dir_all(cache_grammar_dir)?; // let the builder re-clone\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Whenever you change a grammar 'git' URL in extension.toml, delete that grammar's cache dir in the same change","Never hand-copy directories into the grammar cache; let the builder clone them","Treat this error as a cache/manifest mismatch, not a source problem"],"tags":["zed","extension","grammar","git","cache","remote-url"],"backgroundTag":"git-remote-url-mismatch","analyzedSha":"f4178619acd0d47ea1f76a2025c42962c6d6638c","analyzedAt":"2026-08-20T19:29:52.058Z","contentChangedAt":"2026-08-20T19:29:52.058Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}