{"record":{"id":"b5c28db335c2d752","repo":"Billionmail/BillionMail","slug":"contact-d-missing-website-url","errorCode":null,"errorMessage":"contact %d missing website_url","messagePattern":"contact (.+?) missing website_url","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/video_gen/orchestrator.go","lineNumber":161,"sourceCode":"\tg.Log().Infof(ctx, \"video job %d starting pipeline for contact %d <%s>\", job.ID, job.ContactID, job.ContactEmail)\n\ttmpDir := fmt.Sprintf(\"/tmp/video_gen_%d\", job.ID)\n\tif err := os.MkdirAll(tmpDir, 0755); err != nil {\n\t\tmarkFailed(ctx, job.ID, \"setup\", err)\n\t\treturn err\n\t}\n\n\t// 1. Load contact attribs\n\tattribs, err := loadContactAttribs(ctx, job.ContactID, job.GroupID)\n\tif err != nil {\n\t\tmarkFailed(ctx, job.ID, \"load_contact\", err)\n\t\treturn err\n\t}\n\n\twebsiteURL := attribs[\"website_url\"]\n\tbusinessName := attribs[\"business_name\"]\n\townerName := attribs[\"owner_name\"]\n\tif websiteURL == \"\" {\n\t\terr = fmt.Errorf(\"contact %d missing website_url\", job.ContactID)\n\t\tmarkFailed(ctx, job.ID, \"load_contact\", err)\n\t\treturn err\n\t}\n\n\t// Parse signals\n\tsignals := parseSignals(attribs[\"lead_signals\"])\n\n\t// 2. Screenshots + Annotate\n\tupdateJobStatus(ctx, job.ID, JobProcessing, \"screenshot\", \"\")\n\tscreenshots, err := withRetry(func() (*ScreenshotResult, error) {\n\t\treturn CaptureScreenshots(ctx, DefaultScreenshotConfig(websiteURL, businessName, tmpDir))\n\t})\n\tif err != nil {\n\t\tmarkFailed(ctx, job.ID, \"screenshot\", err)\n\t\treturn err\n\t}\n\n\tupdateJobStatus(ctx, job.ID, JobProcessing, \"annotate\", \"\")","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/video_gen/orchestrator.go#L143-L179","documentation":"RunPipeline validates that the contact's attribs map contains a non-empty website_url before proceeding with screenshot capture. The video generation pipeline (screenshots, script, landing page) is built around the contact's website, so without website_url the job cannot proceed. The error is raised after loadContactAttribs succeeded, meaning the contact row exists and has attribs, but the attribs JSON lacks the website_url key or has it set to an empty string.","triggerScenarios":"RunPipeline calls loadContactAttribs(contactID, groupID), gets a valid map, but attribs[\"website_url\"] is empty — i.e. the bm_contacts.attribs JSON either omits the website_url field or stores \"\".","commonSituations":"Contacts imported via CSV/Excel without the website_url column mapped; contacts created manually by users who skipped the website field; upstream enrichment jobs that failed silently and wrote attribs without website_url; leads sourced from channels (e.g. Google Maps scraping) where the website was not present on the source record.","solutions":["Check the contact row in bm_contacts (SELECT attribs FROM bm_contacts WHERE id=<contactID> AND group_id=<groupID>) and confirm website_url exists and is non-empty","Fix the import/enrichment process so website_url is always populated before enqueuing video jobs","Add a pre-enqueue guard in EnqueueVideoJob or the caller that validates attribs[\"website_url\"] != \"\" and skips/fails the contact early instead of consuming a pipeline slot","If the website is genuinely unknown, exclude such contacts from video generation campaigns"],"exampleFix":"// before\nresult, err := EnqueueVideoJob(ctx, contact.ID, contact.Email, groupID)\n// after\nattribs, _ := loadContactAttribs(ctx, contact.ID, groupID)\nif attribs[\"website_url\"] == \"\" {\n    return fmt.Errorf(\"skip contact %d: no website_url\", contact.ID)\n}\nresult, err := EnqueueVideoJob(ctx, contact.ID, contact.Email, groupID)","handlingStrategy":"validation","validationCode":"attribs, err := loadContactAttribs(ctx, contactID, groupID)\nif err != nil { return err }\nif strings.TrimSpace(attribs[\"website_url\"]) == \"\" {\n    return fmt.Errorf(\"contact %d has no website_url; not eligible for video generation\", contactID)\n}","typeGuard":null,"tryCatchPattern":"if err := RunPipeline(ctx, job); err != nil {\n    var missing *MissingFieldError\n    if errors.As(err, &missing) && missing.Field == \"website_url\" {\n        // skip this contact permanently, don't retry\n        skipContact(job.ContactID, \"no website_url\")\n    }\n}","preventionTips":["Make website_url a required column/field at contact import time with validation on write","Validate attribs in EnqueueVideoJob before inserting a pending job","Monitor the bm_video_jobs error column for 'missing website_url' to catch import gaps early","Give the campaign UI a filter to exclude contacts without a website"],"tags":["video-gen","validation","missing-field","contacts"],"backgroundTag":"missing-required-argument","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}