{"record":{"id":"6dd1cc35010a0057","repo":"hasura/graphql-engine","slug":"error-checking-move-target-dir-q-w","errorCode":null,"errorMessage":"error checking move target dir %q: %w","messagePattern":"error checking move target dir %q: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/plugins/move.go","lineNumber":253,"sourceCode":"\t\t}()\n\n\t\treturn errors.E(\n\t\t\top,\n\t\t\tfmt.Errorf(\"could not rename/copy directory %q to %q: %w\", tmp, installDir, err),\n\t\t)\n\t}\n\n\treturn nil\n}\n\n// renameOrCopy will try to rename a dir or file. If rename is not supported, a manual copy will be performed.\n// Existing files at \"to\" will be deleted.\nfunc renameOrCopy(from, to string) error {\n\tvar op errors.Op = \"plugins.renameOrCopy\"\n\n\tfi, err := os.Stat(to)\n\tif err != nil && !stderrors.Is(err, fs.ErrNotExist) {\n\t\treturn errors.E(op, fmt.Errorf(\"error checking move target dir %q: %w\", to, err))\n\t}\n\n\tif fi != nil && fi.IsDir() {\n\t\terr := os.RemoveAll(to)\n\t\tif err != nil {\n\t\t\treturn errors.E(op, fmt.Errorf(\"error cleaning up dir %q: %w\", to, err))\n\t\t}\n\t}\n\n\terr = os.Rename(from, to)\n\t// Fallback for invalid cross-device link (errno:18).\n\tif isCrossDeviceRenameErr(err) {\n\t\tcopyErr := copyTree(from, to)\n\t\tif copyErr != nil {\n\t\t\treturn errors.E(\n\t\t\t\top,\n\t\t\t\tfmt.Errorf(\"failed to copy directory tree as a fallback: %w\", copyErr),\n\t\t\t)","sourceCodeStart":235,"sourceCodeEnd":271,"githubUrl":"https://github.com/hasura/graphql-engine/blob/724551b9ae87845594ef0408cff0e50eb6c90dc5/cli/plugins/move.go#L235-L271","documentation":"renameOrCopy first stats the destination to decide whether it must be removed; os.Stat returned an error other than fs.ErrNotExist (which is expected and ignored). This means an unexpected filesystem-level failure inspecting the destination — permission on a parent directory, I/O error, or name-too-long.","triggerScenarios":"os.Stat(to) failing with EACCES (search permission missing on a parent dir), EIO, ENAMETOOLONG, or similar, on the destination path of a file move or the final install dir rename.","commonSituations":"Install dir parents with restrictive modes; deeply nested paths exceeding NAME_MAX; failing disk/NFS mounts; SELinux/AppArmor denials on the plugins path.","solutions":["Check the quoted destination path: verify each parent directory is searchable (x permission) by the current user","Shorten / normalize the install path if ENAMETOOLONG","Investigate dmesg / mount health for I/O errors or MAC denials on that filesystem","Fix permissions, then retry the plugin install"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if _, err := os.Stat(to); err != nil && !errors.Is(err, fs.ErrNotExist) {\n    return fmt.Errorf(\"destination not statable: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"var perr *fs.PathError\nif errors.As(err, &perr) {\n    switch {\n    case os.IsPermission(perr): // fix search perms on parents\n    case errors.Is(perr, fs.ErrNotExist): // treat as fresh install\n    }\n}","preventionTips":["Keep parent directories of install paths searchable (o+x)","Avoid pathologically long install paths","Monitor disk/mount health on the plugins filesystem"],"tags":["go","filesystem","stat","permissions"],"backgroundTag":"stat-permission-denied","analyzedSha":"724551b9ae87845594ef0408cff0e50eb6c90dc5","analyzedAt":"2026-08-28T07:32:55.105Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}