{"record":{"id":"a5980a36609c6478","repo":"docker/cli","slug":"error-closing-temp-file-w","errorCode":null,"errorMessage":"error closing temp file: %w","messagePattern":"error closing temp file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/config/configfile/file.go","lineNumber":226,"sourceCode":"\t\treturn err\n\t}\n\tdefer func() {\n\t\t// ignore error as the file may already be closed when we reach this.\n\t\t_ = temp.Close()\n\t\tif retErr != nil {\n\t\t\tif err := os.Remove(temp.Name()); err != nil {\n\t\t\t\tlogrus.WithError(err).WithField(\"file\", temp.Name()).Debug(\"Error cleaning up temp file\")\n\t\t\t}\n\t\t}\n\t}()\n\n\terr = c.SaveToWriter(temp)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := temp.Close(); err != nil {\n\t\treturn fmt.Errorf(\"error closing temp file: %w\", err)\n\t}\n\n\t// Handle situation where the configfile is a symlink, and allow for dangling symlinks\n\tcfgFile := c.Filename\n\tif f, err := filepath.EvalSymlinks(cfgFile); err == nil {\n\t\tcfgFile = f\n\t} else if os.IsNotExist(err) {\n\t\t// extract the path from the error if the configfile does not exist or is a dangling symlink\n\t\tvar pathError *os.PathError\n\t\tif errors.As(err, &pathError) {\n\t\t\tcfgFile = pathError.Path\n\t\t}\n\t}\n\n\t// Try copying the current config file (if any) ownership and permissions\n\tcopyFilePermissions(cfgFile, temp.Name())\n\treturn os.Rename(temp.Name(), cfgFile)\n}","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/cli/config/configfile/file.go#L208-L244","documentation":"Returned by ConfigFile.Save (config.go:225-227) when closing the temp file fails after writing the marshaled config. Save writes to a temp file in the config dir then atomically renames it over the real config; a close error here is wrapped with this message and aborts the rename, so the original config is preserved.","triggerScenarios":"ConfigFile.Save -> SaveToWriter(temp) succeeds -> temp.Close() returns an error. Causes are filesystem-level: disk full during flush, NFS/quota errors, or the temp file handle being invalidated.","commonSituations":"The filesystem holding `~/.docker/config.json` runs out of space or inodes mid-write, has a network/NFS hiccup, or imposes a quota. Because Save uses an atomic temp+rename, this error means the write was aborted safely — the existing config.json is not corrupted.","solutions":["Free disk/inode space on the volume holding the config dir (`df -h`, `df -i`).","Check for filesystem errors or quota limits on the user's home directory.","Retry the operation that triggered the save (e.g. `docker login`) once space is available.","Inspect dmesg/system logs for I/O errors on the underlying device."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Fail fast if the target volume is out of space before Save.\nif stat, err := os.StatFile(dir); err == nil {\n    // caller can check df/stat as appropriate to platform\n    _ = stat\n}","typeGuard":null,"tryCatchPattern":"if err := configFile.Save(); err != nil {\n    return fmt.Errorf(\"could not save docker config (disk full?): %w\", err)\n}","preventionTips":["Monitor free disk/inodes on volumes holding `~/.docker`.","Treat a Save failure as non-destructive — the original config.json is untouched due to atomic temp+rename.","Retry the operation after clearing disk pressure."],"tags":["config","filesystem","save","config-json","atomic-write"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}