{"record":{"id":"242e8790d2941386","repo":"Billionmail/BillionMail","slug":"required-column-email-not-found","errorCode":null,"errorMessage":"required column 'email' not found","messagePattern":"required column 'email' not found","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/controller/contact/contact_v1_import_contacts.go","lineNumber":120,"sourceCode":"\treader.LazyQuotes = true\n\treader.TrimLeadingSpace = true\n\n\t// Read headers\n\theaders, err := reader.Read()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read CSV headers: %v\", err)\n\t}\n\n\t// Find the index of the required columns\n\tcolumnIndexes := make(map[string]int)\n\tfor i, header := range headers {\n\t\theader = strings.ToLower(strings.TrimSpace(header))\n\t\tcolumnIndexes[header] = i\n\t}\n\n\t// Check the required columns\n\tif _, ok := columnIndexes[\"email\"]; !ok {\n\t\treturn nil, fmt.Errorf(\"required column 'email' not found\")\n\t}\n\n\t// Process data rows\n\tfor {\n\t\trecord, err := reader.Read()\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\tg.Log().Debugf(ctx, \"Error reading CSV record: %v\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\temail := strings.TrimSpace(record[columnIndexes[\"email\"]])\n\t\tif email == \"\" {\n\t\t\tcontinue\n\t\t}\n","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/controller/contact/contact_v1_import_contacts.go#L102-L138","documentation":"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.","triggerScenarios":"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.","commonSituations":"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').","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)."],"exampleFix":"// before\nName,Email Address,Company\nJohn,john@x.com,Acme\n// after\nname,email,company\nJohn,john@x.com,Acme","handlingStrategy":"validation","validationCode":"r := csv.NewReader(f)\nheaders, err := r.Read()\nif err != nil { return err }\nnorm := make([]string, len(headers))\nfor i, h := range headers {\n\tnorm[i] = strings.ToLower(strings.TrimSpace(h))\n}\nif !slices.Contains(norm, \"email\") {\n\treturn fmt.Errorf(\"CSV must contain an 'email' column; got: %v\", norm)\n}","typeGuard":null,"tryCatchPattern":"contacts, err := importContacts(ctx, file)\nif err != nil {\n\tif strings.Contains(err.Error(), \"required column 'email'\") {\n\t\treturn fmt.Errorf(\"upload rejected: add an 'email' header column to your CSV\")\n\t}\n\treturn err\n}","preventionTips":["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"],"tags":["csv","validation","missing-column"],"backgroundTag":"missing-required-column","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}