{"record":{"id":"61dec5137c39a393","repo":"Billionmail/BillionMail","slug":"create-lipsync-request-w","errorCode":null,"errorMessage":"create lipsync request: %w","messagePattern":"create lipsync request: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/video_gen/lipsync.go","lineNumber":82,"sourceCode":"\t\tOutputDir: outputDir,\n\t}\n}\n\n// BuildLipSyncRequest constructs the HTTP request for lip sync generation.\n// Exported for testing without making API calls.\nfunc BuildLipSyncRequest(cfg LipSyncConfig, req LipSyncRequest) (*http.Request, error) {\n\tif req.Model == \"\" {\n\t\treq.Model = \"sync-1.7.1-beta\"\n\t}\n\n\tbody, err := json.Marshal(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"marshal lipsync request: %w\", err)\n\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","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/video_gen/lipsync.go#L64-L100","documentation":"BuildLipSyncRequest wraps any error returned by http.NewRequest when constructing the outbound POST to the lipsync provider (cfg.lipSyncBase()+lipSyncPath). http.NewRequest only fails on an invalid HTTP method or an unparsable URL, so this error almost always means the configured base URL is malformed or the resulting URL is invalid. The original error is preserved via %w for errors.Is/As inspection.","triggerScenarios":"Calling SubmitLipSync (which calls BuildLipSyncRequest) with a LipSyncConfig whose base URL is empty, missing a scheme (e.g. \"api.example.com\" instead of \"https://api.example.com\"), or contains invalid characters, spaces, or control characters; also if lipSyncBase() concatenation produces a syntactically invalid URL.","commonSituations":"LIPSYNC_BASE_URL env var set incorrectly or left blank while code builds a relative URL; trailing whitespace or quotes pasted into a config file; URL containing unescaped characters like '{', '|', or a space; misconfigured YAML/JSON config where the value was never interpolated.","solutions":["Check the lipsync base URL configuration value (env/config) and ensure it includes a valid scheme, e.g. https://api.provider.com","Trim whitespace/quotes from the configured URL before constructing LipSyncConfig","Run url.Parse on the candidate base URL at config-load/startup time and fail fast with a clear message","Log or unwrap the underlying error (errors.Unwrap / %v of the wrapped error) to see url.Parse's exact complaint"],"exampleFix":"// before\ncfg := LipSyncConfig{BaseURL: os.Getenv(\"LIPSYNC_BASE_URL\")} // empty -> invalid URL\n// after\nbase := strings.TrimSpace(os.Getenv(\"LIPSYNC_BASE_URL\"))\nif u, err := url.Parse(base); err != nil || u.Scheme == \"\" || u.Host == \"\" {\n    return fmt.Errorf(\"invalid LIPSYNC_BASE_URL %q: %w\", base, err)\n}","handlingStrategy":"validation","validationCode":"u, err := url.Parse(cfg.BaseURL)\nif err != nil || u.Scheme == \"\" || u.Host == \"\" {\n    return fmt.Errorf(\"invalid lipsync base URL %q\", cfg.BaseURL)\n}","typeGuard":"func validURL(s string) bool {\n    u, err := url.Parse(s)\n    return err == nil && (u.Scheme == \"http\" || u.Scheme == \"https\") && u.Host != \"\"\n}","tryCatchPattern":"req, err := BuildLipSyncRequest(cfg, input)\nif err != nil {\n    return fmt.Errorf(\"lipsync submit aborted: %w\", err)\n}","preventionTips":["Validate base URLs (scheme + host) once at config load / startup, fail fast","Trim whitespace and quotes from env-sourced URLs","Add a unit test constructing a request from a default config","Keep provider base URLs in one config struct, never scattered string concat"],"tags":["http","url","config","lipsync"],"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"}