{"record":{"id":"1d2c3b8cb66a6ab7","repo":"Billionmail/BillionMail","slug":"create-status-request-w","errorCode":null,"errorMessage":"create status request: %w","messagePattern":"create status request: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/video_gen/lipsync.go","lineNumber":96,"sourceCode":"\t}\n\n\thttpReq, err := http.NewRequest(\"POST\", cfg.lipSyncBase()+lipSyncPath, bytes.NewReader(body))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"create lipsync request: %w\", err)\n\t}\n\n\thttpReq.Header.Set(\"x-api-key\", cfg.APIKey)\n\thttpReq.Header.Set(\"Content-Type\", \"application/json\")\n\treturn httpReq, nil\n}\n\n// BuildLipSyncStatusRequest constructs the HTTP request to check job status.\n// Exported for testing.\nfunc BuildLipSyncStatusRequest(cfg LipSyncConfig, jobID string) (*http.Request, error) {\n\turl := fmt.Sprintf(\"%s%s/%s\", cfg.lipSyncBase(), lipSyncPath, jobID)\n\thttpReq, err := http.NewRequest(\"GET\", url, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"create status request: %w\", err)\n\t}\n\n\thttpReq.Header.Set(\"x-api-key\", cfg.APIKey)\n\treturn httpReq, nil\n}\n\n// SubmitLipSync submits a lip sync job and returns the job ID.\nfunc SubmitLipSync(ctx context.Context, cfg LipSyncConfig, audioURL, videoURL string) (string, error) {\n\treq := LipSyncRequest{\n\t\tAudioURL:       audioURL,\n\t\tVideoURL:       videoURL,\n\t\tSynergizeAudio: true,\n\t}\n\n\thttpReq, err := BuildLipSyncRequest(cfg, req)\n\tif err != nil {\n\t\treturn \"\", err\n\t}","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/video_gen/lipsync.go#L78-L114","documentation":"BuildLipSyncStatusRequest wraps errors from http.NewRequest when constructing the GET status URL \"%s%s/%s\" (base + lipSyncPath + jobID). As with [450], http.NewRequest fails only on bad method or unparsable URL, so a malformed base URL is the usual cause; an embedded path segment with invalid characters (often from jobID) also triggers it.","triggerScenarios":"Calling CheckLipSyncStatus with a LipSyncConfig whose base URL is empty/malformed, or a jobID containing characters that make url.Parse fail (spaces, control chars, raw non-ASCII) since jobID is interpolated into the URL path without escaping.","commonSituations":"Base URL env var misconfigured the same way as [450]; storing/logging a jobID that included whitespace or a full URL and passing it back unescaped; copy-pasted base URL with trailing newline.","solutions":["Validate the base URL has scheme+host at config load time","Sanitize the jobID: trim whitespace and ensure it matches the expected ID format before calling CheckLipSyncStatus","url.PathEscape the jobID (or validate it as alphanumeric) before interpolation","Unwrap the returned error to read url.Parse's specific message"],"exampleFix":"// before\nresp, err := CheckLipSyncStatus(ctx, cfg, jobIDFromDB) // jobID may contain spaces\n// after\njobID = strings.TrimSpace(jobIDFromDB)\nif !validJobIDRe.MatchString(jobID) {\n    return fmt.Errorf(\"invalid lipsync jobID %q\", jobID)\n}\nresp, err := CheckLipSyncStatus(ctx, cfg, jobID)","handlingStrategy":"validation","validationCode":"jobID = strings.TrimSpace(jobID)\nif !regexp.MustCompile(`^[A-Za-z0-9_-]+$`).MatchString(jobID) {\n    return fmt.Errorf(\"invalid lipsync jobID %q\", jobID)\n}","typeGuard":"func validJobID(id string) bool {\n    id = strings.TrimSpace(id)\n    return id != \"\" && !strings.ContainsAny(id, \" /\\\\?#%{}\")\n}","tryCatchPattern":"req, err := BuildLipSyncStatusRequest(cfg, jobID)\nif err != nil {\n    return nil, fmt.Errorf(\"status check aborted for job %q: %w\", jobID, err)\n}","preventionTips":["Sanitize jobIDs when storing/retrieving them; never pass user-supplied strings raw into URLs","Reuse the same base-URL validation as the submit path","Test the empty-jobID path (the existing TestBuildLipSyncStatusRequest_EmptyJobID) in CI"],"tags":["http","url","lipsync","jobid"],"backgroundTag":"invalid-url","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"}