{"record":{"id":"3a15065e03af3d53","repo":"hasura/graphql-engine","slug":"failed-to-remove-old-symlink-w","errorCode":null,"errorMessage":"failed to remove old symlink: %w","messagePattern":"failed to remove old symlink: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/plugins/util.go","lineNumber":184,"sourceCode":"\n\treturn nil\n}\n\n// IsWindows sees runtime.GOOS to find out if current execution mode is win32.\nfunc IsWindows() bool {\n\tgoos := runtime.GOOS\n\n\treturn goos == \"windows\"\n}\n\nfunc createOrUpdateLink(binDir, binary, plugin string) error {\n\tvar op errors.Op = \"plugins.createOrUpdateLink\"\n\n\tdst := filepath.Join(binDir, PluginNameToBin(plugin, IsWindows()))\n\n\terr := removeLink(dst)\n\tif err != nil {\n\t\treturn errors.E(op, fmt.Errorf(\"failed to remove old symlink: %w\", err))\n\t}\n\n\tif _, err := os.Stat(binary); stderrors.Is(err, fs.ErrNotExist) {\n\t\treturn errors.E(\n\t\t\top,\n\t\t\tfmt.Errorf(\n\t\t\t\t\"can't create symbolic link, source binary (%q) cannot be found in extracted archive: %w\",\n\t\t\t\tbinary,\n\t\t\t\terr,\n\t\t\t),\n\t\t)\n\t}\n\n\t// Create new\n\terr = os.Symlink(binary, dst)\n\tif err != nil {\n\t\tif IsWindows() {\n\t\t\t// If cloning the symlink fails on Windows because the user","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/hasura/graphql-engine/blob/724551b9ae87845594ef0408cff0e50eb6c90dc5/cli/plugins/util.go#L166-L202","documentation":"createOrUpdateLink first removes any existing symlink at dst (filepath.Join(binDir, PluginNameToBin(plugin, IsWindows()))) via removeLink before installing the new one. If removeLink fails — because dst exists as a real file/dir, permissions block unlink, or binDir is not writable — this error wraps the OS failure.","triggerScenarios":"installPlugin → createOrUpdateLink where binDir/<plugin>(.exe) exists as a non-symlink file, or the user lacks write permission on binDir, so os.Remove/os.Lstat in removeLink returns an error.","commonSituations":"A previous manual copy of the binary into the bin dir; a directory sitting where the link should go; bin dir owned by root after sudo installs; Windows/WSL path quirks.","solutions":["Inspect the wrapped OS error (EACCES vs EISDIR vs ENOTDIR) to identify the cause.","Manually remove the stale entry: rm <binDir>/<plugin-bin> (or rmdir if it's a directory).","Ensure binDir is writable by the current user (chown/chmod), or configure a user-local bin dir."],"exampleFix":"# before\n$ ls ~/.cli/bin/myplugin   # regular file from old manual install\n# install fails: failed to remove old symlink\n\n# after\n$ rm ~/.cli/bin/myplugin\n$ cli plugin install myplugin","handlingStrategy":"try-catch","validationCode":"dst := filepath.Join(binDir, pluginBinaryName)\nif fi, err := os.Lstat(dst); err == nil && fi.Mode()&os.ModeSymlink == 0 {\n\t// a real file/dir occupies the link path — remove or relocate it first\n\tif err := os.RemoveAll(dst); err != nil {\n\t\treturn err\n\t}\n}","typeGuard":null,"tryCatchPattern":"if err := plugins.Install(...); err != nil {\n\tif errors.Is(err, fs.ErrPermission) && strings.Contains(err.Error(), \"remove old symlink\") {\n\t\t// instruct user to fix bin dir permissions or remove stale file\n\t}\n}","preventionTips":["Keep the CLI bin dir owned by the invoking user; avoid mixing sudo and non-sudo installs.","Never manually copy plugin binaries into the managed bin dir.","On install failures, clean the stale link target and re-run install."],"tags":["plugins","symlink","filesystem","permissions","install"],"backgroundTag":"symlink-creation-failed","analyzedSha":"724551b9ae87845594ef0408cff0e50eb6c90dc5","analyzedAt":"2026-08-28T07:32:55.105Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}