Billionmail/BillionMail · error

failed to write content to zip for %s: %v

Error message

failed to write content to zip for %s: %v

What it means

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).

Source

Thrown at core/internal/controller/contact/contact_v1_export_contacts.go:223

	Contacts int    // Contact count
}

// createZipFileInMemory creates a ZIP file in memory
func createZipFileInMemory(files []ExportFile) ([]byte, error) {
	var buf bytes.Buffer
	zipWriter := zip.NewWriter(&buf)

	for _, file := range files {
		// Create zip entry
		zipEntry, err := zipWriter.Create(file.Name)
		if err != nil {
			return nil, fmt.Errorf("failed to create zip entry for %s: %v", file.Name, err)
		}

		// Write file content to zip
		_, err = zipEntry.Write([]byte(file.Content))
		if err != nil {
			return nil, fmt.Errorf("failed to write content to zip for %s: %v", file.Name, err)
		}
	}

	err := zipWriter.Close()
	if err != nil {
		return nil, fmt.Errorf("failed to close zip writer: %v", err)
	}

	return buf.Bytes(), nil
}

// exportContactsToCSV exports contacts to CSV format
func exportContactsToCSV(contacts []*entity.Contact) (string, error) {
	var buf bytes.Buffer
	writer := csv.NewWriter(&buf)

	// Write CSV headers
	headers := []string{

View on GitHub (pinned to fc36c76c05)

Solutions

  1. 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.
  2. Keep wrapping with %w and include file.Name (already done) so the failing entry is identifiable.
  3. For very large exports, stream the ZIP to the HTTP response instead of buffering entirely in memory.
  4. Check buf.Bytes() length and contact count before responding to catch silent truncation.
  5. Avoid closing shared buffers or the zip.Writer before all entries are written.

Example fix

// before
_, err = zipEntry.Write([]byte(file.Content))
if err != nil {
    return nil, fmt.Errorf("failed to write content to zip for %s: %v", file.Name, err)
}
// after
if _, err = zipEntry.Write([]byte(file.Content)); err != nil {
    return nil, fmt.Errorf("failed to write content to zip for %s: %w", file.Name, err)
}
if err = zipEntry.Flush(); err != nil {
    return nil, fmt.Errorf("failed to flush content to zip for %s: %w", file.Name, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before building the zip, cap and check payload size:
const maxExportBytes = 64 << 20
for _, f := range files {
    if len(f.Content) > maxExportBytes { return errors.New("export file too large") }
}

Try / catch

zipBytes, err := createZipFileInMemory(files)
if err != nil {
    if strings.Contains(err.Error(), "failed to write content") {
        return fmt.Errorf("export aborted while writing %v — check storage and retry", err)
    }
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/5b422c59d164d51e. Report an issue: GitHub.