{"record":{"id":"34cb7371301962d9","repo":"hasura/graphql-engine","slug":"could-not-uninstall-symlink-of-plugin-w","errorCode":null,"errorMessage":"could not uninstall symlink of plugin: %w","messagePattern":"could not uninstall symlink of plugin: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/plugins/plugins.go","lineNumber":253,"sourceCode":"\n// Uninstall will uninstall a plugin.\nfunc (c *Config) Uninstall(name string) error {\n\tvar op errors.Op = \"plugins.Config.Uninstall\"\n\n\tif _, err := c.LoadManifest(c.Paths.PluginInstallReceiptPath(name)); err != nil {\n\t\tif stderrors.Is(err, fs.ErrNotExist) {\n\t\t\treturn errors.E(op, ErrIsNotInstalled)\n\t\t}\n\n\t\treturn errors.E(\n\t\t\top,\n\t\t\tfmt.Errorf(\"failed to look up install receipt for plugin %q: %w\", name, err),\n\t\t)\n\t}\n\n\tsymlinkPath := filepath.Join(c.Paths.BinPath(), PluginNameToBin(name, IsWindows()))\n\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)","sourceCodeStart":235,"sourceCodeEnd":271,"githubUrl":"https://github.com/hasura/graphql-engine/blob/724551b9ae87845594ef0408cff0e50eb6c90dc5/cli/plugins/plugins.go#L235-L271","documentation":"Uninstall() removes the symlink/shim the installer placed in the bin directory (BinPath()/PluginNameToBin(name)) via removeLink. If that removal fails, this error is returned and the plugin directory and receipt are left untouched, so the uninstall is incomplete but restartable.","triggerScenarios":"Calling Uninstall when the symlink in the bin directory cannot be removed: permission denied (bin dir owned by root or another user), the path exists but is a real file on Windows where a shim was expected, or the path is a non-empty directory removeLink refuses to delete.","commonSituations":"CLI was installed via sudo so the bin dir is root-owned; a user replaced the symlink with a real binary; Windows-specific shim/file mismatch; the bin directory is on a read-only mount.","solutions":["Check what exists at BinPath()/PluginNameToBin(name) (ls -la) — if it is a real file or directory rather than a symlink, remove it manually","Fix ownership/permissions of the bin directory (chown -R $(whoami) or run once with appropriate privileges) and retry uninstall","On Windows, ensure no process is running the plugin binary and that the file is not locked","Re-run Uninstall afterwards — the operation is idempotent once the path is clear"],"exampleFix":"# before\n$ ls -la ~/.cli/bin/myplugin   # regular file left by manual copy\n$ rm ~/.cli/bin/myplugin\n# after: uninstall succeeds","handlingStrategy":"try-catch","validationCode":"binTarget := filepath.Join(cfg.Paths.BinPath(), PluginNameToBin(name, IsWindows()))\nif fi, err := os.Lstat(binTarget); err == nil && fi.Mode()&os.ModeSymlink == 0 {\n\t// a real file/dir sits where the symlink should be; remove it first\n\t_ = os.RemoveAll(binTarget)\n}","typeGuard":null,"tryCatchPattern":"if err := cfg.Uninstall(name); err != nil && strings.Contains(err.Error(), \"could not uninstall symlink\") {\n\t// inspect and remove the path at BinPath manually, then retry Uninstall\n}","preventionTips":["Never replace the generated bin symlink with a copied binary","Keep bin directory ownership consistent with the user running the CLI","On Windows, close processes using the plugin binary before uninstalling"],"tags":["plugins","uninstall","symlink","permissions"],"backgroundTag":"symlink-removal-failed","analyzedSha":"724551b9ae87845594ef0408cff0e50eb6c90dc5","analyzedAt":"2026-08-28T07:32:55.105Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}