hasura/graphql-engine · warning

could not remove plugin receipt %q: %w

Error message

could not remove plugin receipt %q: %w

What it means

This is the final step of Uninstall(): deleting the receipt JSON file with os.Remove. If it fails, the symlink and plugin directory are already gone, but the leftover receipt makes the CLI still consider the plugin installed (Uninstall will retry rather than report not-installed).

Source

Thrown at cli/plugins/plugins.go:270

	if err := removeLink(symlinkPath); err != nil {
		return errors.E(op, fmt.Errorf("could not uninstall symlink of plugin: %w", err))
	}

	pluginInstallPath := c.Paths.PluginInstallPath(name)
	if err := os.RemoveAll(pluginInstallPath); err != nil {
		return errors.E(
			op,
			fmt.Errorf("could not remove plugin directory %q: %w", pluginInstallPath, err),
		)
	}

	pluginReceiptPath := c.Paths.PluginInstallReceiptPath(name)

	err := os.Remove(pluginReceiptPath)
	if err != nil {
		return errors.E(
			op,
			fmt.Errorf("could not remove plugin receipt %q: %w", pluginReceiptPath, err),
		)
	}

	return nil
}

func (c *Config) installPlugin(plugin Plugin, platform Platform) error {
	var op errors.Op = "plugins.Config.installPlugin"

	downloadStagingDir := filepath.Join(c.Paths.DownloadPath(), plugin.Name)
	installDir := c.Paths.PluginVersionInstallPath(plugin.Name, plugin.Version)
	binDir := c.Paths.BinPath()

	// check if install dir already exists
	_, err := os.Stat(installDir)
	if err != nil {
		// Download and extract
		err := os.MkdirAll(downloadStagingDir, 0o755)

View on GitHub (pinned to 724551b9ae)

Solutions

  1. Check permissions on the plugins data directory and the receipt file; chown/chmod so the current user can delete it
  2. Remove lingering processes or tools locking the file (sync clients, editors, AV) and retry
  3. Manually delete the receipt file (rm) so the plugin stops appearing installed
  4. Verify no directory now exists at the receipt path; if one does, remove it with rm -rf

Example fix

# before: uninstall removes files but receipt lingers
$ rm "$DATA/plugins/myplugin.json"
# after: plugin fully uninstalled
Defensive patterns

Strategy: fallback

Validate before calling

receiptPath := cfg.Paths.PluginInstallReceiptPath(name)
if _, err := os.Lstat(receiptPath); err == nil {
	// ensure we can delete it; if not, fix perms now
	_ = os.Chmod(receiptPath, 0o644)
}

Try / catch

if err := cfg.Uninstall(name); err != nil && strings.Contains(err.Error(), "could not remove plugin receipt") {
	// symlink and dir are already gone; delete the receipt manually to finish:
	_ = os.Remove(receiptPath)
}

Prevention

When it happens

Trigger: os.Remove of the receipt path fails: permission denied on the plugins data directory, receipt file locked by another process, or the receipt path was replaced by a directory after the earlier LoadManifest succeeded.

Common situations: Plugins data dir owned by root (mixed sudo/non-sudo usage); file lock from sync/AV software; user re-created the receipt as a directory; read-only home directory.

Related errors


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