{"record":{"id":"3968b08a50a191cd","repo":"gofiber/fiber","slug":"failed-to-write-response-body-to-file-w","errorCode":null,"errorMessage":"failed to write response body to file: %w","messagePattern":"failed to write response body to file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/response.go","lineNumber":212,"sourceCode":"\t\t\tif !errors.Is(err, fs.ErrNotExist) {\n\t\t\t\treturn fmt.Errorf(\"failed to check directory: %w\", err)\n\t\t\t}\n\n\t\t\tif err = os.MkdirAll(dir, 0o750); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to create directory: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\t// Create and write to file\n\t\toutFile, err := os.Create(file)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create file: %w\", err)\n\t\t}\n\t\tdefer func() { _ = outFile.Close() }() //nolint:errcheck // not needed\n\n\t\t// Use BodyStream() which handles both streaming and non-streaming cases\n\t\tif _, err = io.Copy(outFile, r.BodyStream()); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to write response body to file: %w\", err)\n\t\t}\n\n\t\treturn nil\n\n\tcase io.Writer:\n\t\t// Use BodyStream() which handles both streaming and non-streaming cases\n\t\tif _, err := io.Copy(p, r.BodyStream()); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to write response body to writer: %w\", err)\n\t\t}\n\t\t// Close the writer if it implements io.WriteCloser\n\t\tif pc, ok := p.(io.WriteCloser); ok {\n\t\t\t_ = pc.Close() //nolint:errcheck // not needed\n\t\t}\n\n\t\treturn nil\n\n\tdefault:\n\t\treturn ErrNotSupportSaveMethod","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/client/response.go#L194-L230","documentation":"After creating the file, Response.Save copies the response body stream into it with io.Copy. This wraps a failure of that copy: a read error from the response body (connection reset, decompression fault, premature EOF) or a write error to the file (disk full, I/O error).","triggerScenarios":"The server connection drops mid-download; decompression fails; disk fills up while writing; the file's underlying device errors.","commonSituations":"Large downloads onto a near-full disk; flaky upstream connections; streaming responses where the server closes early; antivirus/EDR intercepting the write.","solutions":["Check the wrapped cause: io.ErrUnexpectedEOF / syscall.ECONNRESET implies network, syscall.ENOSPC implies disk.","Retry downloads on transient network errors; verify available disk space first for large files.","Stream to a temp file and rename atomically on success to avoid partial outputs."],"exampleFix":"// before\nif err := resp.Save(dst); err != nil { ... }\n\n// after\ntmp := dst + \".part\"\nif err := resp.Save(tmp); err != nil { ... }\nif err := os.Rename(tmp, dst); err != nil { ... }","handlingStrategy":"retry","validationCode":"free, _ := diskFreeBytes(dstDir)\nif free < int64(resp.Size()) {\n    return errors.New(\"insufficient disk space for download\")\n}","typeGuard":null,"tryCatchPattern":"for attempt := 0; attempt < 3; attempt++ {\n    if err := resp.Save(tmp); err == nil { return os.Rename(tmp, dst) }\n    var uerr *url.Error\n    if !errors.As(err, &uerr) && !errors.Is(err, io.ErrUnexpectedEOF) { return err }\n}","preventionTips":["Stream to a temp file and rename atomically on success to avoid partial outputs.","Verify disk space before large downloads.","Retry only transient network causes; surface disk-full errors immediately."],"tags":["client","response","save","io","download","disk"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}