{"record":{"id":"ed0de2c15479594b","repo":"Billionmail/BillionMail","slug":"create-clone-request-w","errorCode":null,"errorMessage":"create clone request: %w","messagePattern":"create clone request: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/video_gen/voice.go","lineNumber":104,"sourceCode":"func DefaultTTSOutputFormat() TTSOutputFormat {\n\treturn TTSOutputFormat{\n\t\tContainer:  \"wav\",\n\t\tSampleRate: 44100,\n\t\tEncoding:   \"pcm_f32le\",\n\t}\n}\n\n// BuildCloneRequest constructs the HTTP request for voice cloning.\n// Exported for testing without making API calls.\nfunc BuildCloneRequest(cfg VoiceConfig, req VoiceCloneRequest) (*http.Request, error) {\n\tbody, err := json.Marshal(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"marshal clone request: %w\", err)\n\t}\n\n\thttpReq, err := http.NewRequest(\"POST\", cfg.voiceBaseURL()+cartesiaClonePath, bytes.NewReader(body))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"create clone request: %w\", err)\n\t}\n\n\thttpReq.Header.Set(\"X-API-Key\", cfg.APIKey)\n\thttpReq.Header.Set(\"Cartesia-Version\", cartesiaAPIVersion)\n\thttpReq.Header.Set(\"Content-Type\", \"application/json\")\n\treturn httpReq, nil\n}\n\n// BuildTTSRequest constructs the HTTP request for text-to-speech.\n// Exported for testing without making API calls.\nfunc BuildTTSRequest(cfg VoiceConfig, req TTSRequest) (*http.Request, error) {\n\tbody, err := json.Marshal(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"marshal TTS request: %w\", err)\n\t}\n\n\thttpReq, err := http.NewRequest(\"POST\", cfg.voiceBaseURL()+cartesiaTTSPath, bytes.NewReader(body))\n\tif err != nil {","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/video_gen/voice.go#L86-L122","documentation":"After marshaling the body, BuildCloneRequest calls http.NewRequest(\"POST\", cfg.voiceBaseURL()+cartesiaClonePath, ...); an invalid URL or method makes NewRequest return a *url.Error which is wrapped as 'create clone request: %w'. This indicates the Cartesia base URL configured in VoiceConfig is malformed.","triggerScenarios":"http.NewRequest fails because cfg.voiceBaseURL() produces an unparseable URL — empty base URL, control characters, spaces, or a bad scheme — so the POST target cfg.voiceBaseURL()+cartesiaClonePath cannot be parsed (TestBuildCloneRequest_EmptyAPIKey exercises config edge cases; the URL itself is the failure point here).","commonSituations":"Missing CARTESIA_API_URL-style env var leaving the base URL empty; a base URL set with a trailing newline/space from config parsing; scheme typo like 'htp://'; URL assembled from untrimmed user config.","solutions":["Log/print cfg.voiceBaseURL()+cartesiaClonePath and run url.Parse on it to see the exact parse failure","Set the Cartesia base URL to the correct default (https://api.cartesia.ai) via the VoiceConfig/env var, trimmed of whitespace","TrimSpace and validate with url.Parse (require scheme http/https) when constructing VoiceConfig from environment","Add a startup config check that fails fast on an unparseable voice base URL"],"exampleFix":"// before\ncfg := VoiceConfig{BaseURL: os.Getenv(\"CARTESIA_URL\")} // \"\" or \"https://api.cartesia.ai\\n\"\nhttpReq, err := http.NewRequest(\"POST\", cfg.voiceBaseURL()+cartesiaClonePath, bytes.NewReader(body))\n// after\nbase := strings.TrimSpace(os.Getenv(\"CARTESIA_URL\"))\nif base == \"\" {\n    base = \"https://api.cartesia.ai\"\n}\nif _, perr := url.Parse(base + cartesiaClonePath); perr != nil {\n    return nil, fmt.Errorf(\"invalid cartesia base URL %q: %w\", base, perr)\n}\ncfg := VoiceConfig{BaseURL: base}\nhttpReq, err := http.NewRequest(\"POST\", cfg.voiceBaseURL()+cartesiaClonePath, bytes.NewReader(body))","handlingStrategy":"validation","validationCode":"func validateVoiceBaseURL(cfg VoiceConfig) error {\n    u, err := url.Parse(cfg.voiceBaseURL() + cartesiaClonePath)\n    if err != nil || u.Scheme == \"\" || u.Host == \"\" {\n        return fmt.Errorf(\"invalid cartesia base URL: %q\", cfg.voiceBaseURL())\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"httpReq, err := video_gen.BuildCloneRequest(cfg, req)\nif err != nil {\n    var urlErr *url.Error\n    if errors.As(err, &urlErr) {\n        log.Errorf(\"bad clone endpoint URL: %v\", urlErr)\n    }\n    return err\n}","preventionTips":["TrimSpace env/config values before storing them in VoiceConfig","Default the base URL to https://api.cartesia.ai when unset","Fail fast at config load with a url.Parse check","Use one shared validated base-URL for clone and TTS paths"],"tags":["http","url","cartesia","config"],"backgroundTag":"invalid-url","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"}