hasura/graphql-engine · error · errors.Error
write file: %w
Error message
write file: %w
What it means
The write method of rawGlobalConfig failed to persist the marshaled config bytes to disk via os.WriteFile. This is an I/O error: the target path is unwritable, a parent directory is missing, or permissions deny creation/truncation of the file. It is thrown after a successful marshal, so the data itself is valid; only writing failed.
Source
Thrown at cli/global_config.go:105
if c.CLIEnvironment == "" {
c.CLIEnvironment = DefaultEnvironment
}
return nil
}
func (c *rawGlobalConfig) write(filename string) error {
var op errors.Op = "cli.rawGlobalConfig.write"
b, err := json.MarshalIndent(c, "", " ")
if err != nil {
return errors.E(op, fmt.Errorf("marshal file: %w", err))
}
err = os.WriteFile(filename, b, 0o644)
if err != nil {
return errors.E(op, fmt.Errorf("write file: %w", err))
}
return nil
}
// setupGlobConfig ensures that global config directory and file exists and
// reads it into the GlobalConfig object.
func (ec *ExecutionContext) setupGlobalConfig() error {
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))
}
View on GitHub (pinned to 724551b9ae)
Solutions
- Check permissions/ownership of the config file and its parent directory (ls -l, chown/chmod) and make the dir writable
- Verify HOME (or the configured GlobalConfigFile path) is writable: touch the path manually to reproduce
- If on a read-only filesystem, point GlobalConfigDir/GlobalConfigFile to a writable mount or tmpfs
- Ensure the parent directory exists before writing (MkdirAll runs earlier for the default dir, but a custom file path may bypass it)
Example fix
// before ec.GlobalConfigFile = "/etc/app/config.json" // after cfgDir := filepath.Join(os.TempDir(), "app") _ = os.MkdirAll(cfgDir, 0o755) ec.GlobalConfigFile = filepath.Join(cfgDir, "config.json")
Defensive patterns
Strategy: validation
Validate before calling
if dir := filepath.Dir(ec.GlobalConfigFile); dir != "" {
if info, err := os.Stat(dir); err != nil || !info.IsDir() {
_ = os.MkdirAll(dir, 0o755)
}
}
if f, err := os.OpenFile(ec.GlobalConfigFile, os.O_WRONLY|os.O_CREATE, 0o644); err != nil {
return fmt.Errorf("config path not writable: %w", err)
}
_ = f.Close() Type guard
func isWritable(path string) bool {
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o644)
if err != nil {
return false
}
_ = f.Close()
return true
} Try / catch
if err := ec.Prepare(ctx); err != nil {
if strings.Contains(err.Error(), "write file:") {
log.Printf("config path %s not writable; redirecting to temp dir", ec.GlobalConfigFile)
ec.GlobalConfigFile = filepath.Join(os.TempDir(), GlobalConfigFileName)
return ec.Prepare(ctx)
}
return err
} Prevention
- Always set GlobalConfigDir/GlobalConfigFile explicitly to a writable location instead of relying on defaults
- Pre-check writability with OpenFile before Prepare
- In containers, mount a writable volume at the config dir
- Avoid running the CLI against root-owned config paths without matching privileges
When it happens
Trigger: Calling setupGlobalConfig (via Prepare) when ec.GlobalConfigFile points to a path that does not exist, is on a read-only filesystem, or is owned by another user (e.g. running as non-root against a root-owned config file, or an unwritable $HOME).
Common situations: Running the CLI in a container with a read-only root filesystem, HOME set to a non-writable dir, a leftover config file created by root (permission denied on truncate), or a custom GlobalConfigFile pointing into a directory that was never created.
Related errors
- cannot create global config directory: %w
- write global config file: %w
- writing global config file failed: %w
- writing metadata to file: %w
- error getting directory details: %w
AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28).
Data as JSON: /api/errors/a7fb0b863b2087ff.
Report an issue: GitHub.