{"record":{"id":"5ad5ca50a65cd9cc","repo":"Billionmail/BillionMail","slug":"failed-to-load-email-templates-v","errorCode":null,"errorMessage":"failed to load email templates: %v","messagePattern":"failed to load email templates: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/batch_mail/api_mail_send.go","lineNumber":386,"sourceCode":"\t\tWhereIn(\"id\", apiIds).\n\t\tCtx(ctx).\n\t\tScan(&apiTemplates)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to load API templates: %v\", err)\n\t}\n\t// Collect template IDs\n\ttemplateIds := make([]int, 0, len(apiTemplates))\n\tfor _, t := range apiTemplates {\n\t\ttemplateIds = append(templateIds, t.TemplateId)\n\t}\n\t// Batch query email templates\n\tvar emailTemplates []entity.EmailTemplate\n\terr = g.DB().Model(\"email_templates\").\n\t\tWhereIn(\"id\", templateIds).\n\t\tCtx(ctx).\n\t\tScan(&emailTemplates)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to load email templates: %v\", err)\n\t}\n\t// Batch query contacts\n\tvar contacts []entity.Contact\n\terr = g.DB().Model(\"bm_contacts\").\n\t\tWhereIn(\"email\", recipientEmails).\n\t\tCtx(ctx).\n\t\tScan(&contacts)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to load contacts: %v\", err)\n\t}\n\t// Build cache\n\tcache := &CacheData{\n\t\tApiTemplates:   make(map[int]entity.ApiTemplates),\n\t\tEmailTemplates: make(map[int]entity.EmailTemplate),\n\t\tContacts:       make(map[string]entity.Contact),\n\t}\n\n\tfor _, t := range apiTemplates {","sourceCodeStart":368,"sourceCodeEnd":404,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/batch_mail/api_mail_send.go#L368-L404","documentation":"preloadData loads email_templates rows for the template IDs collected from the API templates. A failure of the SELECT on email_templates is wrapped as \"failed to load email templates: %v\". It indicates a database-level error during the read, not that a template row is absent.","triggerScenarios":"ProcessApiMailQueue -> preloadData when the SELECT ... WHERE id IN (templateIds) on email_templates fails: DB unreachable, permissions, schema mismatch, or the earlier api_templates query succeeded but the connection dropped between queries.","commonSituations":"Connection reset mid-batch under heavy load; migration renamed columns on email_templates; DB user denied SELECT on that table; Postgres failover during processing.","solutions":["Inspect the wrapped underlying error in logs to identify the DB failure cause","Confirm email_templates table schema and run pending migrations","Check DB connectivity/credentials and retry queue processing","Ensure connection pool limits are adequate for concurrent batch workers"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"if len(templateIds) == 0 {\n    return nil, errors.New(\"no template ids collected from API templates\")\n}","typeGuard":null,"tryCatchPattern":"cache, err := preloadData(ctx, apiIds)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to load email templates\") {\n        // log underlying cause, requeue for retry\n    }\n    return err\n}","preventionTips":["Run schema migrations before deploying queue processors","Add DB connection retry/backoff at startup","Alert on email_templates query errors distinct from missing-template cases"],"tags":["database","postgresql","query"],"backgroundTag":"database-query-failed","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"}