{"record":{"id":"f3942e0ca7b439d7","repo":"hasura/graphql-engine","slug":"failed-to-ensure-create-directory-q-w","errorCode":null,"errorMessage":"failed to ensure create directory %q: %w","messagePattern":"failed to ensure create directory %q: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/plugins/util.go","lineNumber":51,"sourceCode":"\n\t// windowsForbidden is taken from  https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file\n\twindowsForbidden = []string{\n\t\t\"CON\", \"PRN\", \"AUX\", \"NUL\", \"COM1\", \"COM2\",\n\t\t\"COM3\", \"COM4\", \"COM5\", \"COM6\", \"COM7\", \"COM8\", \"COM9\", \"LPT1\", \"LPT2\",\n\t\t\"LPT3\", \"LPT4\", \"LPT5\", \"LPT6\", \"LPT7\", \"LPT8\", \"LPT9\",\n\t}\n)\n\nfunc isValidSHA256(s string) bool { return validSHA256.MatchString(s) }\n\n// ensureDirs makes sure the paths created.\nfunc ensureDirs(paths ...string) error {\n\tvar op errors.Op = \"plugins.ensureDirs\"\n\n\tfor _, p := range paths {\n\t\terr := os.MkdirAll(p, 0o755)\n\t\tif err != nil {\n\t\t\treturn errors.E(op, fmt.Errorf(\"failed to ensure create directory %q: %w\", p, err))\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// IsSafePluginName checks if the plugin Name is safe to use.\nfunc IsSafePluginName(name string) bool {\n\tif !safePluginRegexp.MatchString(name) {\n\t\treturn false\n\t}\n\n\tfor _, forbidden := range windowsForbidden {\n\t\tif strings.EqualFold(forbidden, name) {\n\t\t\treturn false\n\t\t}\n\t}\n","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/hasura/graphql-engine/blob/724551b9ae87845594ef0408cff0e50eb6c90dc5/cli/plugins/util.go#L33-L69","documentation":"ensureDirs runs os.MkdirAll(path, 0o755) for each path passed to it (called from Prepare before plugin install). If any MkdirAll fails — permission denied, a path component is an existing file, or an I/O error — this error wraps the failing path and the underlying OS error.","triggerScenarios":"Prepare → ensureDirs hitting: a parent directory in the path owned by another user (EACCES), an existing regular file where a directory is expected (ENOTDIR), a read-only filesystem, or an invalid path string.","commonSituations":"Installing a plugin into a bin/plugins dir under $HOME with wrong ownership after running the CLI with sudo; a config file sitting where a directory should be; disk full or read-only mounts in containers.","solutions":["Check the path in the message: if a file exists where a directory is expected, move/remove it.","Fix ownership/permissions: mkdir -p the directory yourself and chown it to the running user (sudo chown -R $(whoami) <dir>).","If read-only (container/snap), point the plugin dir at a writable location via configuration."],"exampleFix":"# before\n$ plugin-cli install ... # EACCES on /usr/local/lib/plugins\n\n# after\n$ sudo mkdir -p /usr/local/lib/plugins && sudo chown -R $(whoami) /usr/local/lib/plugins\n$ plugin-cli install ...","handlingStrategy":"try-catch","validationCode":"for _, dir := range []string{pluginDir, binDir} {\n\tif fi, err := os.Stat(dir); err == nil && !fi.IsDir() {\n\t\treturn fmt.Errorf(\"%s exists and is not a directory\", dir)\n\t}\n\tif err := os.MkdirAll(dir, 0o755); err != nil {\n\t\treturn fmt.Errorf(\"pre-create %s: %w\", dir, err)\n\t}\n}","typeGuard":null,"tryCatchPattern":"if err := plugins.Prepare(...); err != nil {\n\tif errors.Is(err, fs.ErrPermission) {\n\t\t// prompt user to fix ownership or choose a user-writable dir\n\t}\n}","preventionTips":["Pre-create the plugin/bin directories with correct ownership during setup.","Never run the installer against dirs owned by root unless the process runs as root.","On read-only environments, configure a writable plugin directory."],"tags":["plugins","filesystem","permissions","install"],"backgroundTag":"mkdir-permission-denied","analyzedSha":"724551b9ae87845594ef0408cff0e50eb6c90dc5","analyzedAt":"2026-08-28T07:32:55.105Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}