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

  1. Identify which side changed recently: the extension's extension.toml version or the built-in extensions registry entry.
  2. Make both values identical (usually bump the registry entry to the new manifest version).
  3. 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

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


AI-assisted analysis of zed-industries/zed@9d272b0363 (2026-08-20). Data as JSON: /api/errors/9e24089f3fd2d430. Report an issue: GitHub.