{"record":{"id":"5b15d3c90ecaf5bb","repo":"chenhg5/cc-connect","slug":"qwen-tts-create-download-request-w","errorCode":null,"errorMessage":"qwen tts: create download request: %w","messagePattern":"qwen tts: create download request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/tts.go","lineNumber":182,"sourceCode":"\t\t\tAudio struct {\n\t\t\t\tURL string `json:\"url\"`\n\t\t\t} `json:\"audio\"`\n\t\t} `json:\"output\"`\n\t}\n\tif err := json.Unmarshal(body, &result); err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"qwen tts: parse response: %w\", err)\n\t}\n\tif result.Code != \"\" {\n\t\treturn nil, \"\", fmt.Errorf(\"qwen tts API error %s: %s\", result.Code, result.Message)\n\t}\n\tif result.Output.Audio.URL == \"\" {\n\t\treturn nil, \"\", fmt.Errorf(\"qwen tts: empty audio URL in response\")\n\t}\n\n\t// Download WAV from temporary URL\n\taudioReq, err := http.NewRequestWithContext(ctx, http.MethodGet, result.Output.Audio.URL, nil)\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"qwen tts: create download request: %w\", err)\n\t}\n\taudioResp, err := q.Client.Do(audioReq)\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"qwen tts: download audio: %w\", err)\n\t}\n\tdefer audioResp.Body.Close()\n\n\twavData, err := io.ReadAll(audioResp.Body)\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"qwen tts: read audio: %w\", err)\n\t}\n\treturn wavData, \"wav\", nil\n}\n\n// ──────────────────────────────────────────────────────────────\n// OpenAITTS — OpenAI-compatible TTS implementation (P1)\n// ──────────────────────────────────────────────────────────────\n","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/tts.go#L164-L200","documentation":"After QwenTTS.Synthesize obtains a temporary audio URL from DashScope (core/tts.go:182), it builds a GET request with http.NewRequestWithContext to download the WAV file. This error wraps any failure of http.NewRequestWithContext itself — almost always a malformed or unparseable URL string returned by the API.","triggerScenarios":"The URL string in result.Output.Audio.URL is not a valid absolute HTTP URL (e.g. empty scheme, malformed characters, control characters, or the API returned a relative/garbage value) so net/http cannot construct the request.","commonSituations":"A misbehaving or mocked DashScope endpoint returning an invalid URL, URL encoding issues when the temporary URL contains characters that need escaping, or a custom/proxied BaseURL altering what the API returns as the audio location.","solutions":["Log result.Output.Audio.URL when this error occurs and validate it is a well-formed absolute http/https URL","Use net/url.Parse on the URL before creating the request to fail with a clearer message","Re-run synthesis to get a fresh temporary URL — if the API persistently returns invalid URLs, report/check the provider","Ensure no custom proxy layer is rewriting the audio URL into a non-absolute form"],"exampleFix":"// before\naudioReq, err := http.NewRequestWithContext(ctx, http.MethodGet, result.Output.Audio.URL, nil)\nif err != nil {\n    return nil, \"\", fmt.Errorf(\"qwen tts: create download request: %w\", err)\n}\n// after\nu, perr := url.Parse(result.Output.Audio.URL)\nif perr != nil || (u.Scheme != \"http\" && u.Scheme != \"https\") {\n    return nil, \"\", fmt.Errorf(\"qwen tts: invalid audio URL %q: %w\", result.Output.Audio.URL, perr)\n}\naudioReq, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)\nif err != nil {\n    return nil, \"\", fmt.Errorf(\"qwen tts: create download request: %w\", err)\n}","handlingStrategy":"validation","validationCode":"u, err := url.Parse(audioURL)\nif err != nil || (u.Scheme != \"http\" && u.Scheme != \"https\") || u.Host == \"\" {\n    // don't build a request; reject before http.NewRequestWithContext\n    return fmt.Errorf(\"invalid audio URL: %q\", audioURL)\n}","typeGuard":"func isValidAbsURL(s string) bool {\n    u, err := url.Parse(s)\n    return err == nil && (u.Scheme == \"http\" || u.Scheme == \"https\") && u.Host != \"\"\n}","tryCatchPattern":"audio, format, err := tts.Synthesize(ctx, text, opts)\nif err != nil && strings.Contains(err.Error(), \"create download request\") {\n    return fmt.Errorf(\"tts provider returned unusable audio URL: %w\", err)\n}","preventionTips":["Validate the URL with net/url.Parse before constructing the request","Log the offending URL to detect provider-side schema issues quickly","Retry once before surfacing, in case of a transient malformed response"],"tags":["tts","http","url","qwen","dashscope"],"backgroundTag":"invalid-url-format","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}