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
- Check permissions on the plugins data directory and the receipt file; chown/chmod so the current user can delete it
- Remove lingering processes or tools locking the file (sync clients, editors, AV) and retry
- Manually delete the receipt file (rm) so the plugin stops appearing installed
- 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
- After this error, only the receipt remains — deleting it manually completes the uninstall
- Keep the plugins data dir writable by the installing user
- Watch for sync/AV tools locking JSON files in the data dir
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
- installation receipt could not be stored, uninstall may fail
- failed to look up install receipt for plugin %q: %w
- could not remove plugin directory %q: %w
- write plugin receipt %q: %w
- could not uninstall symlink of plugin: %w
AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28).
Data as JSON: /api/errors/c6078368a7b408ea.
Report an issue: GitHub.