{"record":{"id":"3c59b68450c19cb9","repo":"Billionmail/BillionMail","slug":"check-lip-sync-status-w","errorCode":null,"errorMessage":"check lip sync status: %w","messagePattern":"check lip sync status: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/video_gen/orchestrator.go","lineNumber":409,"sourceCode":"\n\tif defaultID != \"\" {\n\t\treturn defaultID, nil\n\t}\n\n\treturn \"\", fmt.Errorf(\"no voice configured: set VOICE_SAMPLE_URL or VOICE_DEFAULT_ID\")\n}\n\n// pollLipSync polls lip sync status until completed or timeout.\nfunc pollLipSync(ctx context.Context, cfg LipSyncConfig, jobID string) (string, error) {\n\tdeadline := time.Now().Add(lipSyncTimeout)\n\tfor {\n\t\tif time.Now().After(deadline) {\n\t\t\treturn \"\", fmt.Errorf(\"lip sync timed out after %v\", lipSyncTimeout)\n\t\t}\n\n\t\tresp, err := CheckLipSyncStatus(ctx, cfg, jobID)\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"check lip sync status: %w\", err)\n\t\t}\n\n\t\tswitch resp.Status {\n\t\tcase \"completed\":\n\t\t\tif resp.VideoURL == \"\" {\n\t\t\t\treturn \"\", fmt.Errorf(\"lip sync completed but no video URL\")\n\t\t\t}\n\t\t\treturn resp.VideoURL, nil\n\t\tcase \"failed\":\n\t\t\treturn \"\", fmt.Errorf(\"lip sync failed: %s\", resp.Error)\n\t\t}\n\n\t\ttime.Sleep(lipSyncPollDelay)\n\t}\n}\n\n// withRetry retries a function up to maxRetries times with exponential backoff.\nfunc withRetry[T any](fn func() (T, error)) (T, error) {","sourceCodeStart":391,"sourceCodeEnd":427,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/video_gen/orchestrator.go#L391-L427","documentation":"Each poll iteration calls CheckLipSyncStatus; if that HTTP/API call fails, pollLipSync wraps the failure with 'check lip sync status: %w' and aborts immediately rather than retrying. The wrapped cause is the actual network/API error.","triggerScenarios":"Any failure of CheckLipSyncStatus during polling: HTTP 4xx/5xx from the provider, invalid API key, DNS/network failure, context cancellation, or malformed provider response.","commonSituations":"Expired or missing provider API key; provider 500s mid-job; network blips in the container; ctx cancelled because the parent RunPipeline request timed out.","solutions":["Read the wrapped cause (%w) to identify the underlying provider error","Verify the lip-sync API key and endpoint configuration","Retry the pipeline; add retry/backoff around status checks for transient errors","Check ctx cancellation: if the parent request timed out, extend the caller's deadline"],"exampleFix":"// before\nresp, err := CheckLipSyncStatus(ctx, cfg, jobID)\nif err != nil {\n\treturn \"\", fmt.Errorf(\"check lip sync status: %w\", err)\n}\n// after\nresp, err := CheckLipSyncStatus(ctx, cfg, jobID)\nif err != nil {\n\tif errors.Is(err, context.Canceled) { return \"\", err }\n\t// transient: keep polling instead of failing\n\ttime.Sleep(lipSyncPollDelay)\n\tcontinue\n}","handlingStrategy":"try-catch","validationCode":"if cfg.APIKey == \"\" {\n\treturn fmt.Errorf(\"lip sync API key not configured\")\n}\nif err := ctx.Err(); err != nil {\n\treturn err\n}","typeGuard":"func isTransientLipSyncErr(err error) bool {\n\treturn err != nil && !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded)\n}","tryCatchPattern":"url, err := pollLipSync(ctx, cfg, jobID)\nvar apiErr *APIStatusError\nif errors.As(err, &apiErr) && apiErr.Code >= 500 {\n\t// transient upstream error — safe to retry the whole stage\n}","preventionTips":["Rotate and verify the provider API key in all environments","Add retry/backoff around status checks for 5xx and network errors","Check ctx deadlines: pass a context with enough budget for the full poll loop","Monitor the provider status page and set alerts"],"tags":["network","api-error","polling","wrapped-error"],"backgroundTag":"upstream-api-error","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"}