{"record":{"id":"a7fb0b863b2087ff","repo":"hasura/graphql-engine","slug":"write-file-w","errorCode":null,"errorMessage":"write file: %w","messagePattern":"write file: %w","errorType":"exception","errorClass":"errors.Error","httpStatus":null,"severity":"error","filePath":"cli/global_config.go","lineNumber":105,"sourceCode":"\n\tif c.CLIEnvironment == \"\" {\n\t\tc.CLIEnvironment = DefaultEnvironment\n\t}\n\n\treturn nil\n}\n\nfunc (c *rawGlobalConfig) write(filename string) error {\n\tvar op errors.Op = \"cli.rawGlobalConfig.write\"\n\n\tb, err := json.MarshalIndent(c, \"\", \"  \")\n\tif err != nil {\n\t\treturn errors.E(op, fmt.Errorf(\"marshal file: %w\", err))\n\t}\n\n\terr = os.WriteFile(filename, b, 0o644)\n\tif err != nil {\n\t\treturn errors.E(op, fmt.Errorf(\"write file: %w\", err))\n\t}\n\n\treturn nil\n}\n\n// setupGlobConfig ensures that global config directory and file exists and\n// reads it into the GlobalConfig object.\nfunc (ec *ExecutionContext) setupGlobalConfig() error {\n\tvar op errors.Op = \"cli.ExecutionContext.setupGlobalConfig\"\n\t// check if the directory name is set, else default\n\tif len(ec.GlobalConfigDir) == 0 {\n\t\tec.Logger.Debug(\"global config directory is not pre-set, defaulting\")\n\n\t\thome, err := os.UserHomeDir()\n\t\tif err != nil {\n\t\t\treturn errors.E(op, fmt.Errorf(\"cannot get home directory: %w\", err))\n\t\t}\n","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/hasura/graphql-engine/blob/724551b9ae87845594ef0408cff0e50eb6c90dc5/cli/global_config.go#L87-L123","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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)"],"exampleFix":"// before\nec.GlobalConfigFile = \"/etc/app/config.json\"\n// after\ncfgDir := filepath.Join(os.TempDir(), \"app\")\n_ = os.MkdirAll(cfgDir, 0o755)\nec.GlobalConfigFile = filepath.Join(cfgDir, \"config.json\")","handlingStrategy":"validation","validationCode":"if dir := filepath.Dir(ec.GlobalConfigFile); dir != \"\" {\n    if info, err := os.Stat(dir); err != nil || !info.IsDir() {\n        _ = os.MkdirAll(dir, 0o755)\n    }\n}\nif f, err := os.OpenFile(ec.GlobalConfigFile, os.O_WRONLY|os.O_CREATE, 0o644); err != nil {\n    return fmt.Errorf(\"config path not writable: %w\", err)\n}\n_ = f.Close()","typeGuard":"func isWritable(path string) bool {\n    f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o644)\n    if err != nil {\n        return false\n    }\n    _ = f.Close()\n    return true\n}","tryCatchPattern":"if err := ec.Prepare(ctx); err != nil {\n    if strings.Contains(err.Error(), \"write file:\") {\n        log.Printf(\"config path %s not writable; redirecting to temp dir\", ec.GlobalConfigFile)\n        ec.GlobalConfigFile = filepath.Join(os.TempDir(), GlobalConfigFileName)\n        return ec.Prepare(ctx)\n    }\n    return err\n}","preventionTips":["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"],"tags":["go","filesystem","permissions","config","io-write"],"backgroundTag":"file-write-permission-denied","analyzedSha":"724551b9ae87845594ef0408cff0e50eb6c90dc5","analyzedAt":"2026-08-28T07:32:55.105Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}