zed-industries/zed · error · anyhow::Error
mismatched versions: ({}) != ({})
Error message
mismatched versions: ({}) != ({}) What it means
The headless extension host (used server-side, e.g. in collab) loads each built-in extension and cross-checks the version parsed from the extension's extension.toml against the version recorded in the compiled-in built-in extension registry entry. A mismatch bails with both values so a half-updated build fails fast instead of serving a stale extension.
Source
Thrown at crates/extension_host/src/headless_host.rs:132
.retain(|id| !on_client.contains(id.as_ref()));
let mut to_remove = store
.loaded_extensions
.keys()
.filter(|id| !on_client.contains(id.as_ref()))
.cloned()
.collect::<Vec<Arc<str>>>();
to_remove.extend(store.failed_removals.drain());
let to_load = extensions
.into_iter()
.filter(|extension| {
let is_new = store
.loaded_extensions
.get(extension.id.as_str())
.is_none_or(|loaded| {
loaded.version.as_ref() != extension.version.as_str()
});
extension.dev || is_new
})
.collect::<Vec<ExtensionVersion>>();
(to_remove, to_load)
})?;
let mut extensions_changed = false;
let result = async {
let mut missing = Vec::new();
for extension_id in to_remove {
log::info!("removing extension: {extension_id}");
let (was_loaded, removal) = store.update(cx, |store, cx| {
(
store.loaded_extensions.contains_key(&extension_id),
store.uninstall_extension(&extension_id, cx),
)
})?;
if was_loaded {
extensions_changed = true;View on GitHub (pinned to 9d272b0363)
Solutions
- Identify which side changed recently: the extension's extension.toml version or the built-in extensions registry entry.
- Make both values identical (usually bump the registry entry to the new manifest version).
- cargo clean / rebuild so the compiled-in registry is regenerated, then re-run the headless host.
Example fix
# before: manifest says version = "0.2.0" while the built-in registry entry says "0.1.0" # after: keep them in lockstep — update the registry entry to "0.2.0" (or revert the manifest) and rebuild
Defensive patterns
Strategy: validation
Validate before calling
let manifest = ExtensionManifest::load(fs.clone(), &extension_dir).await?;
anyhow::ensure!(
manifest.version.as_ref() == extension.version.as_str(),
"manifest {} != registry {}: keep both in sync",
manifest.version,
extension.version
); Prevention
- Bump the built-in registry entry and the extension manifest version in the same commit.
- Add a test that loads every built-in extension and asserts version equality with the registry.
- Rebuild fully after pulling changes that touch built-in extensions.
When it happens
Trigger: The version in the extension's manifest was bumped (or reverted) without updating the corresponding version in the built-in extensions list compiled into the host, or vice versa; building with a mismatched extensions checkout/submodule.
Common situations: Release automation that updates extension manifests but not the registry (or the reverse); manual edits to one side; rebasing across an extension version bump; stale build artifacts after pulling.
Related errors
- capability for process:exec {desired_command} {desired_args:
- capability for process:exec {desired_command} {desired_args:
- capability for download_file {desired_url} is not granted by
- capability for npm:install {package_name} is not granted by
- grammar name '{grammar_name}' must be written in snake_case:
AI-assisted analysis of zed-industries/zed@9d272b0363 (2026-08-20).
Data as JSON: /api/errors/9e24089f3fd2d430.
Report an issue: GitHub.