Billionmail/BillionMail · error
required column 'email' not found
Error message
required column 'email' not found
What it means
parseCSVContent validates that the CSV header row contains an 'email' column (header names are lowercased and trimmed before the lookup). This is a precondition check: without an email column the importer cannot map rows to contacts. It is thrown with a plain fmt.Errorf, no wrapped cause.
Source
Thrown at core/internal/controller/contact/contact_v1_import_contacts.go:120
reader.LazyQuotes = true
reader.TrimLeadingSpace = true
// Read headers
headers, err := reader.Read()
if err != nil {
return nil, fmt.Errorf("failed to read CSV headers: %v", err)
}
// Find the index of the required columns
columnIndexes := make(map[string]int)
for i, header := range headers {
header = strings.ToLower(strings.TrimSpace(header))
columnIndexes[header] = i
}
// Check the required columns
if _, ok := columnIndexes["email"]; !ok {
return nil, fmt.Errorf("required column 'email' not found")
}
// Process data rows
for {
record, err := reader.Read()
if err == io.EOF {
break
}
if err != nil {
g.Log().Debugf(ctx, "Error reading CSV record: %v", err)
continue
}
email := strings.TrimSpace(record[columnIndexes["email"]])
if email == "" {
continue
}
View on GitHub (pinned to fc36c76c05)
Solutions
- Rename the header cell to exactly 'email' (case and surrounding whitespace are tolerated, synonyms are not).
- Re-export the CSV ensuring the 'Export column headers' option is enabled.
- If the source uses 'Email Address', post-process the header row before upload or extend the matcher to accept common synonyms.
- Validate the CSV header locally before importing (script or spreadsheet check).
Example fix
// before Name,Email Address,Company John,john@x.com,Acme // after name,email,company John,john@x.com,Acme
Defensive patterns
Strategy: validation
Validate before calling
r := csv.NewReader(f)
headers, err := r.Read()
if err != nil { return err }
norm := make([]string, len(headers))
for i, h := range headers {
norm[i] = strings.ToLower(strings.TrimSpace(h))
}
if !slices.Contains(norm, "email") {
return fmt.Errorf("CSV must contain an 'email' column; got: %v", norm)
} Try / catch
contacts, err := importContacts(ctx, file)
if err != nil {
if strings.Contains(err.Error(), "required column 'email'") {
return fmt.Errorf("upload rejected: add an 'email' header column to your CSV")
}
return err
} Prevention
- Always export with column headers enabled
- Use the exact header name 'email'
- Keep a known-good sample CSV for template imports
- Validate the header row client-side before uploading
When it happens
Trigger: The uploaded CSV's first row contains no cell that normalizes to 'email' — e.g. columns named 'Email Address' with extra characters that don't trim/lower to exactly 'email', 'e-mail', 'mail', or a header row that is actually data because the export skipped headers.
Common situations: Users import exports from other CRMs that label the column 'Email Address' or 'E-mail'; CSV saved without a header row; header row misspelled ('emails'); localized headers (e.g. 'correo').
Related errors
- error reading CSV file: %v
- failed to read CSV headers: %v
- You cannot create more than 5000 batches
- No log files found in the given date range
- invalid start_date: %v
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/242e8790d2941386.
Report an issue: GitHub.