zed-industries/zed · error · anyhow::Error

extension {extension_id} is still installed

Error message

extension {extension_id} is still installed

What it means

install_dev_extension installs a local extension by symlinking extensions/installed/<id> to your source directory. If the target path already exists as a real directory (not a symlink) — i.e. a marketplace-installed extension occupies that slot — it bails rather than destructively removing a real install; only a leftover symlink is auto-removed.

Source

Thrown at crates/extension_host/src/extension_host.rs:1113

        cx: &mut Context<Self>,
    ) -> Task<Result<()>> {
        let extensions_dir = self.extensions_dir();
        let fs = self.fs.clone();
        let builder = self.builder.clone();

        cx.spawn(async move |this, cx| {
            let mut extension_manifest =
                ExtensionManifest::load(fs.clone(), &extension_source_path).await?;
            let extension_id = extension_manifest.id.clone();

            if let Some(uninstall_task) = this
                .update(cx, |this, cx| {
                    this.extension_index
                        .extensions
                        .get(extension_id.as_ref())
                        .is_some_and(|index_entry| !index_entry.dev)
                        .then(|| this.uninstall_extension(extension_id.clone(), cx))
                })
                .ok()
                .flatten()
            {
                uninstall_task.await.log_err();
            }

            if !this.update(cx, |this, cx| {
                match this.outstanding_operations.entry(extension_id.clone()) {
                    btree_map::Entry::Occupied(_) => return false,
                    btree_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Install),
                };
                cx.notify();
                true
            })? {
                return Ok(());
            }

            let _finish = cx.on_drop(&this, {

View on GitHub (pinned to 9d272b0363)

Solutions

  1. Uninstall the marketplace version first via the Extensions page in Zed.
  2. Or manually remove the directory: rm -rf ~/.local/share/zed/extensions/installed/<extension-id> (macOS: ~/Library/Application Support/Zed/...).
  3. Then re-run Install Dev Extension — the fresh symlink will be created.

Example fix

# before: marketplace copy occupies the slot
rm -rf ~/.local/share/zed/extensions/installed/my-extension
# then in Zed:Extensions page
# after: "Install Dev Extension" now symlinks your local source
ln -s ~/src/my-extension ~/.local/share/zed/extensions/installed/my-extension  # what Zed does for you
Defensive patterns

Strategy: validation

Validate before calling

let output_path = extensions_dir.join(extension_id.as_ref());
if let Some(metadata) = fs.metadata(&output_path).await? {
    if !metadata.is_symlink {
        // a real (marketplace) install occupies the slot — uninstall it first
        store.uninstall_extension(extension_id.clone(), cx)?.await?;
    }
}
// safe to create the dev-extension symlink now

Try / catch

match fs.metadata(&output_path).await {
    Ok(metadata) if !metadata.is_symlink => {
        // bail path: tell the user to uninstall the marketplace version first
    }
    Ok(_) => { /* symlink or absent: proceed */ }
    Err(e) if e.io_error_kind() == io::ErrorKind::NotFound => { /* proceed */ }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Using the Install Dev Extension command (or extensions_ui install_dev_extension) for an extension id that is currently installed from the marketplace, so extensions/installed/<id> is a regular directory.

Common situations: Iterating on a fork of an extension you already installed normally; CI machines with a pre-seeded extensions directory; switching between the published version and a local checkout.

Related errors


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