{"record":{"id":"b80f12a0bd59cfc8","repo":"chenhg5/cc-connect","slug":"openai-tts-create-request-w","errorCode":null,"errorMessage":"openai tts: create request: %w","messagePattern":"openai tts: create request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/tts.go","lineNumber":250,"sourceCode":"\t\tvoice = \"alloy\"\n\t}\n\treqBody := map[string]any{\n\t\t\"model\": o.Model,\n\t\t\"input\": text,\n\t\t\"voice\": voice,\n\t}\n\tif opts.Speed > 0 {\n\t\treqBody[\"speed\"] = opts.Speed\n\t}\n\tjsonData, err := json.Marshal(reqBody)\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"openai tts: marshal request: %w\", err)\n\t}\n\n\turl := strings.TrimRight(o.BaseURL, \"/\") + \"/audio/speech\"\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(jsonData))\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"openai tts: create request: %w\", err)\n\t}\n\treq.Header.Set(\"Authorization\", \"Bearer \"+o.APIKey)\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := o.Client.Do(req)\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"openai tts: request: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tbody, _ := io.ReadAll(resp.Body)\n\t\treturn nil, \"\", fmt.Errorf(\"openai tts API %d: %s\", resp.StatusCode, body)\n\t}\n\n\tmp3Data, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"openai tts: read audio: %w\", err)","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/tts.go#L232-L268","documentation":"This error is returned by the OpenAI-compatible TTS Synthesize method when http.NewRequestWithContext fails to construct the POST request to the /audio/speech endpoint. In practice this almost always means the composed URL failed url.Parse validation. The library wraps the underlying error so callers can distinguish request-construction failures from network and API failures.","triggerScenarios":"o.BaseURL contains characters that make the concatenated URL unparseable (spaces, control characters, unencoded specials), or the context ctx passed to Synthesize is already canceled at request-construction time.","commonSituations":"A misconfigured base_url in config.toml with a stray space or newline, an empty/garbage BaseURL value, or passing an already-expired/canceled context into Synthesize.","solutions":["Check the configured BaseURL: it must be a clean absolute URL like https://api.openai.com/v1 with no spaces or stray characters","Trim whitespace/newlines from the BaseURL when loading config","If the cause is context.Canceled/DeadlineExceeded, create the context fresh for each Synthesize call instead of reusing a canceled one"],"exampleFix":"// before\nBaseURL: \"https://api.openai.com/v1 \" // trailing space breaks url parse\n// after\nBaseURL: strings.TrimSpace(\"https://api.openai.com/v1\")","handlingStrategy":"validation","validationCode":"if u, err := url.Parse(strings.TrimSpace(cfg.TTSBaseURL)); err != nil || u.Scheme == \"\" || u.Host == \"\" {\n    return fmt.Errorf(\"invalid tts base_url %q: %w\", cfg.TTSBaseURL, err)\n}","typeGuard":"func validBaseURL(s string) bool {\n    u, err := url.Parse(strings.TrimSpace(s))\n    return err == nil && (u.Scheme == \"https\" || u.Scheme == \"http\") && u.Host != \"\"\n}","tryCatchPattern":"mp3, format, err := tts.Synthesize(ctx, text)\nif err != nil {\n    if strings.Contains(err.Error(), \"create request\") {\n        // configuration problem: check BaseURL\n    }\n    return err\n}","preventionTips":["Trim whitespace from base_url when loading config.toml","Validate the URL parses at startup, before first use","Create a fresh context per Synthesize call"],"tags":["go","tts","http-request","url-validation"],"backgroundTag":"invalid-url","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}