Billionmail/BillionMail · error
load contact attribs: %w
Error message
load contact attribs: %w
What it means
loadContactAttribs wraps the underlying error from the GoFrame DB query (g.DB().Model("bm_contacts")...Value("attribs")) that fetches the attribs column for a given contact id and group_id. This is a database-layer failure, not a data problem: the wrapped error carries the real cause (connectivity, bad SQL, schema drift). Callers (RunPipeline, updateContactVideoAttribs) surface it and the job is marked failed at the load_contact phase.
Source
Thrown at core/internal/service/video_gen/orchestrator.go:329
os.RemoveAll(tmpDir)
return nil
}
// markFailed sets job status to failed with error message and logs it.
func markFailed(ctx context.Context, jobID int, phase string, err error) {
g.Log().Errorf(ctx, "video job %d failed at %s: %v", jobID, phase, err)
updateJobStatus(ctx, jobID, JobFailed, phase, err.Error())
}
// loadContactAttribs fetches attribs for a contact from DB.
func loadContactAttribs(ctx context.Context, contactID, groupID int) (map[string]string, error) {
val, err := g.DB().Model("bm_contacts").Ctx(ctx).
Where("id", contactID).
Where("group_id", groupID).
Value("attribs")
if err != nil {
return nil, fmt.Errorf("load contact attribs: %w", err)
}
if val.IsNil() || val.IsEmpty() {
return nil, fmt.Errorf("contact %d has no attribs", contactID)
}
result := make(map[string]string)
if err := val.Scan(&result); err != nil {
return nil, fmt.Errorf("parse contact attribs: %w", err)
}
return result, nil
}
// updateContactVideoAttribs merges video URLs into Contact.Attribs.
func updateContactVideoAttribs(ctx context.Context, contactID, groupID int, videoURL, thumbURL, landingURL string) error {
// Load existing attribs
attribs, err := loadContactAttribs(ctx, contactID, groupID)
if err != nil {
return errView on GitHub (pinned to fc36c76c05)
Solutions
- Read the wrapped error in the job's error column / logs to identify the root DB cause
- Verify the database is reachable and healthy (pg_isready, connection config in manifest/config)
- Confirm the bm_contacts table exists with an attribs column in the target database
- Check DB user permissions for SELECT on bm_contacts
- Retry after transient outages; consider verifying DB connectivity before launching the pipeline
Example fix
// before
val, err := g.DB().Model("bm_contacts").Ctx(ctx).Where("id", contactID).Where("group_id", groupID).Value("attribs")
// after
val, err := g.DB().Model("bm_contacts").Ctx(ctx).Timeout(10*time.Second).Where("id", contactID).Where("group_id", groupID).Value("attribs")
if err != nil {
g.Log().Errorf(ctx, "db query bm_contacts id=%d group=%d: %v", contactID, groupID, err)
return nil, fmt.Errorf("load contact attribs: %w", err)
} Defensive patterns
Strategy: retry
Validate before calling
if err := g.DB().Ctx(ctx).Ping(ctx); err != nil {
return fmt.Errorf("database unavailable, deferring video jobs: %w", err)
}
// then proceed with the attribs query Try / catch
attribs, err := loadContactAttribs(ctx, job.ContactID, job.GroupID)
if err != nil {
var dbErr interface{ IsDBError() bool }
if errors.As(err, &dbErr) {
// transient DB issue: leave job pending, retry next poll
return err
}
markFailed(ctx, job.ID, "load_contact", err)
return err
} Prevention
- Add DB health checks to deployment readiness probes
- Set sane connection-pool limits and timeouts in GoFrame DB config
- Ensure migrations create bm_contacts before the video-gen worker starts
- Alert on repeated 'load contact attribs' failures — they indicate infra, not data, problems
When it happens
Trigger: The SELECT attribs FROM bm_contacts WHERE id=? AND group_id=? query returns an error: DB unreachable, connection pool exhausted, bm_contacts table missing/renamed, permission denied, or driver/timeout errors.
Common situations: PostgreSQL container down or restarting during a deploy; DATABASE_URL/DB config mispointed in staging; migration hasn't created bm_contacts in a fresh environment; transient network blips between app and DB; too many open connections under load from parallel video jobs.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- enqueue video job: %w
- Failed to get the total number of exception recipients: %w
- Failed to get the exception recipient list: %w
- Failed to add exception recipient: %w
- Failed to remove exception recipient: %w
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/78ee8802479a7877.
Report an issue: GitHub.