{"record":{"id":"c6078368a7b408ea","repo":"hasura/graphql-engine","slug":"could-not-remove-plugin-receipt-q-w","errorCode":null,"errorMessage":"could not remove plugin receipt %q: %w","messagePattern":"could not remove plugin receipt %q: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"cli/plugins/plugins.go","lineNumber":270,"sourceCode":"\tif err := removeLink(symlinkPath); err != nil {\n\t\treturn errors.E(op, fmt.Errorf(\"could not uninstall symlink of plugin: %w\", err))\n\t}\n\n\tpluginInstallPath := c.Paths.PluginInstallPath(name)\n\tif err := os.RemoveAll(pluginInstallPath); err != nil {\n\t\treturn errors.E(\n\t\t\top,\n\t\t\tfmt.Errorf(\"could not remove plugin directory %q: %w\", pluginInstallPath, err),\n\t\t)\n\t}\n\n\tpluginReceiptPath := c.Paths.PluginInstallReceiptPath(name)\n\n\terr := os.Remove(pluginReceiptPath)\n\tif err != nil {\n\t\treturn errors.E(\n\t\t\top,\n\t\t\tfmt.Errorf(\"could not remove plugin receipt %q: %w\", pluginReceiptPath, err),\n\t\t)\n\t}\n\n\treturn nil\n}\n\nfunc (c *Config) installPlugin(plugin Plugin, platform Platform) error {\n\tvar op errors.Op = \"plugins.Config.installPlugin\"\n\n\tdownloadStagingDir := filepath.Join(c.Paths.DownloadPath(), plugin.Name)\n\tinstallDir := c.Paths.PluginVersionInstallPath(plugin.Name, plugin.Version)\n\tbinDir := c.Paths.BinPath()\n\n\t// check if install dir already exists\n\t_, err := os.Stat(installDir)\n\tif err != nil {\n\t\t// Download and extract\n\t\terr := os.MkdirAll(downloadStagingDir, 0o755)","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/hasura/graphql-engine/blob/724551b9ae87845594ef0408cff0e50eb6c90dc5/cli/plugins/plugins.go#L252-L288","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before: uninstall removes files but receipt lingers\n$ rm \"$DATA/plugins/myplugin.json\"\n# after: plugin fully uninstalled","handlingStrategy":"fallback","validationCode":"receiptPath := cfg.Paths.PluginInstallReceiptPath(name)\nif _, err := os.Lstat(receiptPath); err == nil {\n\t// ensure we can delete it; if not, fix perms now\n\t_ = os.Chmod(receiptPath, 0o644)\n}","typeGuard":null,"tryCatchPattern":"if err := cfg.Uninstall(name); err != nil && strings.Contains(err.Error(), \"could not remove plugin receipt\") {\n\t// symlink and dir are already gone; delete the receipt manually to finish:\n\t_ = os.Remove(receiptPath)\n}","preventionTips":["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"],"tags":["plugins","uninstall","receipt","filesystem"],"backgroundTag":"file-removal-failed","analyzedSha":"724551b9ae87845594ef0408cff0e50eb6c90dc5","analyzedAt":"2026-08-28T07:32:55.105Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}