{"record":{"id":"f246f83d485ee75a","repo":"chenhg5/cc-connect","slug":"create-upload-request-w","errorCode":null,"errorMessage":"create upload request: %w","messagePattern":"create upload request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/dingtalk/dingtalk.go","lineNumber":1365,"sourceCode":"\tbody := bytes.NewBuffer(nil)\n\twriter := multipart.NewWriter(body)\n\n\tpart, err := writer.CreateFormFile(\"media\", fileName)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"create form file: %w\", err)\n\t}\n\n\tif _, err := part.Write(data); err != nil {\n\t\treturn \"\", fmt.Errorf(\"write media data: %w\", err)\n\t}\n\n\tif err := writer.Close(); err != nil {\n\t\treturn \"\", fmt.Errorf(\"close multipart writer: %w\", err)\n\t}\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost, uploadURL, body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"create upload request: %w\", err)\n\t}\n\n\treq.Header.Set(\"Content-Type\", writer.FormDataContentType())\n\n\tresp, err := p.httpClient.Do(req)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"upload request: %w\", err)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\trespBody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"read upload response: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"upload returned status %d: %s\", resp.StatusCode, respBody)\n\t}","sourceCodeStart":1347,"sourceCodeEnd":1383,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/dingtalk/dingtalk.go#L1347-L1383","documentation":"http.NewRequestWithContext could not construct the POST request to the DingTalk media upload URL, almost always because the URL failed to parse. Since the URL is built with fmt.Sprintf from the access token and media type, an offending token value or malformed URL is the usual culprit.","triggerScenarios":"uploadMedia builds uploadURL := fmt.Sprintf(\"https://oapi.dingtalk.com/media/upload?access_token=%s&type=%s\", token, mediaType) and token contains characters that break url.Parse (control characters, spaces), or mediaType is empty/invalid.","commonSituations":"getAccessToken returning an error-string or debug placeholder as the token; corrupted config injecting whitespace into credentials; a refactor changing mediaType to an empty value.","solutions":["Log/verify the access token format (it should be an opaque alphanumeric string); re-fetch the token if malformed.","Validate mediaType is a non-empty known value (\"voice\" or \"image\") before building the URL.","Use url.Values to encode query parameters instead of Sprintf to avoid injection/parsing issues.","Check for control characters/whitespace in config-provided credentials and trim them."],"exampleFix":"// before\nuploadURL := fmt.Sprintf(\"https://oapi.dingtalk.com/media/upload?access_token=%s&type=%s\", token, mediaType)\n// after\nq := url.Values{\"access_token\": {token}, \"type\": {mediaType}}\nuploadURL := \"https://oapi.dingtalk.com/media/upload?\" + q.Encode()","handlingStrategy":"validation","validationCode":"if token == \"\" || strings.ContainsAny(token, \" \\t\\r\\n\\x00\") { return errors.New(\"malformed access token\") }\nif mediaType != \"voice\" && mediaType != \"image\" { return errors.New(\"invalid mediaType\") }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Encode query params with url.Values instead of fmt.Sprintf.","Validate token/mediaType before constructing the URL.","Trim whitespace from credentials at config load."],"tags":["dingtalk","http","url","upload"],"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"}