hasura/graphql-engine · error · errors.Error
cannot create global config directory: %w
Error message
cannot create global config directory: %w
What it means
os.MkdirAll(ec.GlobalConfigDir, os.ModePerm) failed while creating the global config directory. This wraps mkdir syscall errors: permission denied on a parent, a path component that exists as a file (ENOTDIR), read-only filesystem, or I/O errors. It fires for both the default (~/.<name>) and any custom GlobalConfigDir.
Source
Thrown at cli/global_config.go:132
var op errors.Op = "cli.ExecutionContext.setupGlobalConfig"
// check if the directory name is set, else default
if len(ec.GlobalConfigDir) == 0 {
ec.Logger.Debug("global config directory is not pre-set, defaulting")
home, err := os.UserHomeDir()
if err != nil {
return errors.E(op, fmt.Errorf("cannot get home directory: %w", err))
}
globalConfigDir := filepath.Join(home, GlobalConfigDirName)
ec.GlobalConfigDir = globalConfigDir
ec.Logger.Debugf("global config directory set as '%s'", ec.GlobalConfigDir)
}
// create the config directory
err := os.MkdirAll(ec.GlobalConfigDir, os.ModePerm)
if err != nil {
return errors.E(op, fmt.Errorf("cannot create global config directory: %w", err))
}
// check if the filename is set, else default
if len(ec.GlobalConfigFile) == 0 {
ec.GlobalConfigFile = filepath.Join(ec.GlobalConfigDir, GlobalConfigFileName)
ec.Logger.Debugf("global config file set as '%s'", ec.GlobalConfigFile)
}
// check if the global config file exist
_, err = os.Stat(ec.GlobalConfigFile)
if stderrors.Is(err, fs.ErrNotExist) {
// file does not exist, teat as first run and create it
ec.Logger.Debug(
"global config file does not exist, this could be the first run, creating it...",
)
// create an empty config object
gc := &rawGlobalConfig{}View on GitHub (pinned to 724551b9ae)
Solutions
- Manually create the directory with correct ownership: mkdir -p + chown
- Check that no path component is a regular file (ls -la each level) and remove/rename it
- Point GlobalConfigDir at a writable location if the filesystem is read-only
- Run with sufficient privileges or fix SELinux/AppArmor labels if enforced
Example fix
// before
ec.GlobalConfigDir = "/etc/mycli"
// after
ec.GlobalConfigDir = filepath.Join(os.Getenv("HOME"), ".mycli") Defensive patterns
Strategy: validation
Validate before calling
if err := os.MkdirAll(ec.GlobalConfigDir, 0o755); err != nil {
return fmt.Errorf("cannot pre-create config dir %s: %w", ec.GlobalConfigDir, err)
} Type guard
func dirIsCreatable(p string) bool {
return os.MkdirAll(p, 0o755) == nil
} Try / catch
if err := ec.Prepare(ctx); err != nil {
if strings.Contains(err.Error(), "cannot create global config directory") {
ec.GlobalConfigDir = filepath.Join(os.TempDir(), GlobalConfigDirName)
return ec.Prepare(ctx)
}
return err
} Prevention
- Pre-create and own the config directory during installation
- Never place GlobalConfigDir under paths owned by root when running unprivileged
- Validate no path component is an existing regular file
When it happens
Trigger: GlobalConfigDir points under a root-owned path while running unprivileged, a path component (e.g. ~/.config) is actually a regular file, or the filesystem is read-only (immutable container, squashfs).
Common situations: Running the CLI as non-root against /etc/... paths, leftover files where directories are expected, read-only rootfs in Kubernetes, or SELinux/AppArmor denying mkdir.
Related errors
- write file: %w
- write global config file: %w
- writing global config file failed: %w
- failed to create move path %q: %w
- error creating directory at %q: %w
AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28).
Data as JSON: /api/errors/a0e80c530268ef25.
Report an issue: GitHub.