{"record":{"id":"5b422c59d164d51e","repo":"Billionmail/BillionMail","slug":"failed-to-write-content-to-zip-for-s-v","errorCode":null,"errorMessage":"failed to write content to zip for %s: %v","messagePattern":"failed to write content to zip for (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/controller/contact/contact_v1_export_contacts.go","lineNumber":223,"sourceCode":"\tContacts int    // Contact count\n}\n\n// createZipFileInMemory creates a ZIP file in memory\nfunc createZipFileInMemory(files []ExportFile) ([]byte, error) {\n\tvar buf bytes.Buffer\n\tzipWriter := zip.NewWriter(&buf)\n\n\tfor _, file := range files {\n\t\t// Create zip entry\n\t\tzipEntry, err := zipWriter.Create(file.Name)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to create zip entry for %s: %v\", file.Name, err)\n\t\t}\n\n\t\t// Write file content to zip\n\t\t_, err = zipEntry.Write([]byte(file.Content))\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to write content to zip for %s: %v\", file.Name, err)\n\t\t}\n\t}\n\n\terr := zipWriter.Close()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to close zip writer: %v\", err)\n\t}\n\n\treturn buf.Bytes(), nil\n}\n\n// exportContactsToCSV exports contacts to CSV format\nfunc exportContactsToCSV(contacts []*entity.Contact) (string, error) {\n\tvar buf bytes.Buffer\n\twriter := csv.NewWriter(&buf)\n\n\t// Write CSV headers\n\theaders := []string{","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/controller/contact/contact_v1_export_contacts.go#L205-L241","documentation":"After an entry writer is obtained from zipWriter.Create, the file bytes are written to it. The returned io.Writer for a ZIP entry only surfaces errors when the underlying buffer write fails; for an in-memory bytes.Buffer this essentially never fails, so this error is practically unreachable in this setup but is guarded for correctness (e.g. if the sink were ever swapped for a file or network stream).","triggerScenarios":"A write to the entry writer returning an error: with bytes.Buffer this would require an unrecoverable condition; with a swapped-out sink (file on disk, HTTP stream) it occurs on ENOSPC/disk-full, permission errors, or broken connections while writing large contact exports.","commonSituations":"Disk full when exporting very large contact groups if the implementation is changed to stream to disk; temp filesystem full in containers; buffered writer closed mid-export after a refactor.","solutions":["With the current in-memory implementation, treat this as defensive code; if it fires after a sink change, check disk space and permissions on the output location.","Keep wrapping with %w and include file.Name (already done) so the failing entry is identifiable.","For very large exports, stream the ZIP to the HTTP response instead of buffering entirely in memory.","Check buf.Bytes() length and contact count before responding to catch silent truncation.","Avoid closing shared buffers or the zip.Writer before all entries are written."],"exampleFix":"// before\n_, err = zipEntry.Write([]byte(file.Content))\nif err != nil {\n    return nil, fmt.Errorf(\"failed to write content to zip for %s: %v\", file.Name, err)\n}\n// after\nif _, err = zipEntry.Write([]byte(file.Content)); err != nil {\n    return nil, fmt.Errorf(\"failed to write content to zip for %s: %w\", file.Name, err)\n}\nif err = zipEntry.Flush(); err != nil {\n    return nil, fmt.Errorf(\"failed to flush content to zip for %s: %w\", file.Name, err)\n}","handlingStrategy":"try-catch","validationCode":"// before building the zip, cap and check payload size:\nconst maxExportBytes = 64 << 20\nfor _, f := range files {\n    if len(f.Content) > maxExportBytes { return errors.New(\"export file too large\") }\n}","typeGuard":null,"tryCatchPattern":"zipBytes, err := createZipFileInMemory(files)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to write content\") {\n        return fmt.Errorf(\"export aborted while writing %v — check storage and retry\", err)\n    }\n    return err\n}","preventionTips":["If the sink ever changes from bytes.Buffer, check disk space / handle ErrShortWrite","Stream large exports to the HTTP response rather than buffering in memory","Check total export size before composing the ZIP to avoid memory exhaustion","Keep the file name in the wrapped error so the failing entry is identifiable"],"tags":["zip","io","export"],"backgroundTag":"zip-write-failed","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}