{"record":{"id":"b655f1136d3ac9dc","repo":"hasura/graphql-engine","slug":"failed-to-remove-the-symlink-in-q-w","errorCode":null,"errorMessage":"failed to remove the symlink in %q: %w","messagePattern":"failed to remove the symlink in %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/plugins/util.go","lineNumber":244,"sourceCode":"}\n\n// removeLink removes a symlink reference if exists.\nfunc removeLink(path string) error {\n\tvar op errors.Op = \"plugins.removeLink\"\n\n\tfi, err := os.Lstat(path)\n\tif stderrors.Is(err, fs.ErrNotExist) {\n\t\treturn nil\n\t} else if err != nil {\n\t\treturn errors.E(op, fmt.Errorf(\"failed to read the symlink in %q: %w\", path, err))\n\t}\n\n\tif fi.Mode()&os.ModeSymlink == 0 && !IsWindows() {\n\t\treturn errors.E(op, fmt.Errorf(\"file %q is not a symlink (mode=%s)\", path, fi.Mode()))\n\t}\n\n\tif err := os.Remove(path); err != nil {\n\t\treturn errors.E(op, fmt.Errorf(\"failed to remove the symlink in %q: %w\", path, err))\n\t}\n\n\treturn nil\n}\n\n// PluginNameToBin creates the name of the symlink file for the plugin name.\n// It converts dashes to underscores.\nfunc PluginNameToBin(name string, isWindows bool) string {\n\tname = strings.ReplaceAll(name, \"-\", \"_\")\n\n\tname = \"hasura-\" + name\n\tif isWindows {\n\t\tname += \".exe\"\n\t}\n\n\treturn name\n}\n","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/hasura/graphql-engine/blob/724551b9ae87845594ef0408cff0e50eb6c90dc5/cli/plugins/util.go#L226-L262","documentation":"After removeLink confirms the target is a symlink, os.Remove is called to delete it; this error wraps any failure of that deletion. It usually means the containing directory is not writable (deleting a file requires write permission on its directory, not on the file itself) or the file vanished between Lstat and Remove.","triggerScenarios":"Calling Uninstall or createOrUpdateLink when os.Remove fails on the symlink: directory lacks write/execute permission, immutable file attribute set (chattr +i), file locked by another process on Windows, or a TOCTOU race where the link was already removed.","commonSituations":"The plugins bin directory is owned by root after running the CLI with sudo once, so later non-root runs cannot delete links; or on Windows an antivirus/terminal holds a handle to the plugin binary.","solutions":["Fix ownership/permissions of the bin directory: sudo chown -R $USER ~/.hasura/plugins && chmod u+w ~/.hasura/plugins/bin.","Check for immutability: lsattr <path> and clear with chattr -i if set.","Close processes holding the binary (or reboot on Windows) and retry the uninstall.","Retry the command if the link was concurrently removed (race)."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// ensure the containing directory is writable before uninstall\nif info, err := os.Stat(filepath.Dir(linkPath)); err == nil {\n    if info.Mode().Perm()&0o200 == 0 { os.Chmod(filepath.Dir(linkPath), 0o755) }\n}","typeGuard":null,"tryCatchPattern":"err := uninstall()\nif err != nil && strings.Contains(err.Error(), \"failed to remove the symlink\") {\n    time.Sleep(100 * time.Millisecond)\n    err = uninstall() // covers TOCTOU races\n}","preventionTips":["Never run the CLI with sudo for routine plugin operations.","Keep antivirus exclusions for the plugins bin directory on Windows."],"tags":["go","filesystem","symlink","delete","permissions"],"backgroundTag":"file-delete-permission-denied","analyzedSha":"724551b9ae87845594ef0408cff0e50eb6c90dc5","analyzedAt":"2026-08-28T07:32:55.105Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}