hasura/graphql-engine · error

failed to link installed plugin: %w

Error message

failed to link installed plugin: %w

What it means

installPlugin finishes by symlinking (or copying, on Windows) the installed plugin binary into the shared bin directory via createOrUpdateLink. This error means that final link step failed — typically because the bin directory cannot be created, the destination link is held open/locked, or a same-named file exists that is not the CLI's link.

Source

Thrown at cli/plugins/plugins.go:354

	if _, ok := IsSubPath(subPathAbs, pathAbs); !ok {
		if err != nil {
			return errors.E(
				op,
				fmt.Errorf(
					"the fullPath %q does not extend the sub-fullPath %q: %w",
					fullPath,
					installDir,
					err,
				),
			)
		}

		return nil
	}

	err = createOrUpdateLink(binDir, fullPath, plugin.Name)
	if err != nil {
		return errors.E(op, fmt.Errorf("failed to link installed plugin: %w", err))
	}

	return nil
}

// Upgrade will reinstall and delete the old plugin. The operation tries
// to not get the plugin dir in a bad state if it fails during the process.
func (c *Config) Upgrade(pluginName string, version *semver.Version) (Plugin, error) {
	var op errors.Op = "plugins.Config.Upgrade"

	ps, err := c.LoadPluginByName(pluginName)
	if err != nil {
		if stderrors.Is(err, fs.ErrNotExist) {
			return Plugin{}, errors.E(
				op,
				fmt.Errorf("plugin %q does not exist in the plugin index", pluginName),
			)
		}

View on GitHub (pinned to 724551b9ae)

Solutions

  1. Check the wrapped error — EACCES/EPERM points to permissions, EEXIST to a conflicting file
  2. Remove any leftover regular file named <plugin>.exe/<plugin> in the configured bin directory and retry
  3. Ensure the bin directory is writable (and on a filesystem supporting symlinks; on Windows enable Developer Mode or run elevated)
  4. Temporarily exclude the bin dir from antivirus scanning if creation is blocked right after extraction
Defensive patterns

Strategy: fallback

Validate before calling

binDir := paths.BinPath()
if err := os.MkdirAll(binDir, 0o755); err != nil { return err }
if info, err := os.Stat(binDir); err != nil || !info.IsDir() { return fmt.Errorf("bin dir unusable") }

Try / catch

if err := plugins.Install(p, plat); err != nil {
    if strings.Contains(err.Error(), "failed to link installed plugin") {
        // clear stale non-symlink file at the link target, then retry once
    }
}

Prevention

When it happens

Trigger: Config.Install or Config.Upgrade completing download/extract, then failing in createOrUpdateLink(binDir, fullPath, plugin.Name): nonexistent or non-writable bin dir, EPERM/EACCES on Windows or restricted filesystems, an existing non-symlink file at the link target, or antivirus locking the freshly extracted binary.

Common situations: Windows file locking or Defender scanning the new binary; a stale regular file (not symlink) named after the plugin in the bin dir; bin directory on a read-only or network mount; missing admin rights on system paths.

Related errors


AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28). Data as JSON: /api/errors/50abe50f56cff6fa. Report an issue: GitHub.