{"record":{"id":"20cfaf003ab3af6d","repo":"sipeed/picoclaw","slug":"invalid-proxy-url-q-w-20cfaf","errorCode":null,"errorMessage":"invalid proxy URL %q: %w","messagePattern":"invalid proxy URL %q: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/channels/weixin/api.go","lineNumber":43,"sourceCode":"type ApiClient struct {\n\tBaseURL    string\n\tToken      string\n\tHttpClient *http.Client\n}\n\nfunc NewApiClient(baseURL, token string, proxy string) (*ApiClient, error) {\n\tif baseURL == \"\" {\n\t\tbaseURL = \"https://ilinkai.weixin.qq.com/\"\n\t}\n\n\tclient := &http.Client{\n\t\t// Default timeout; will be overridden per context\n\t}\n\n\tif proxy != \"\" {\n\t\tproxyURL, err := url.Parse(proxy)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid proxy URL %q: %w\", proxy, err)\n\t\t}\n\n\t\t// Clone the default transport so we preserve all default settings (TLS, HTTP/2, timeouts, keep-alives)\n\t\tif defaultTransport, ok := http.DefaultTransport.(*http.Transport); ok {\n\t\t\ttransport := defaultTransport.Clone()\n\t\t\ttransport.Proxy = http.ProxyURL(proxyURL)\n\t\t\tclient.Transport = transport\n\t\t} else {\n\t\t\t// Fallback: preserve previous behavior if DefaultTransport is not the expected type\n\t\t\tclient.Transport = &http.Transport{\n\t\t\t\tProxy: http.ProxyURL(proxyURL),\n\t\t\t}\n\t\t}\n\t}\n\n\treturn &ApiClient{\n\t\tBaseURL:    baseURL,\n\t\tToken:      token,","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/weixin/api.go#L25-L61","documentation":"NewApiClient for the Weixin iLink API rejects a configured proxy string because url.Parse fails on it. Go's url.Parse almost never errors — it fails on control characters, an embedded NUL, or a malformed percent-escape — so this fires only for genuinely malformed proxy strings, not for a wrong-but-well-formed host. The client refuses to build rather than silently ignoring the proxy.","triggerScenarios":"Constructing the Weixin API client with a non-empty proxy setting containing control characters, a raw newline/NUL, or an invalid percent-encoding (e.g. \"http://proxy:80%zz\" or a value with trailing CR from a config file). Well-formed-but-unreachable proxies do NOT trigger this; they fail later at request time.","commonSituations":"A .env or YAML value with an unescaped newline or trailing \\r (Windows line endings); a percent sign in the proxy password that is not double-encoded; copy-pasting a proxy URL with invisible characters; a DNS name with a stray control byte.","solutions":["Print the proxy string with %q to expose hidden control characters/newlines, then strip or fix them","Percent-encode credentials in the proxy URL correctly (a literal % must be %25)","Validate the proxy URL early in config loading with url.Parse and fail with a clear message at startup","If no proxy is intended, leave the setting empty instead of a placeholder like \"none\""],"exampleFix":"# before\nproxy: \"http://user:pa%ss@proxy.local:8080\"  # invalid % escape -> error 666\n\n# after\nproxy: \"http://user:pa%25ss@proxy.local:8080\"  # % encoded as %25","handlingStrategy":"validation","validationCode":"func validateProxy(proxy string) error {\n    if proxy == \"\" {\n        return nil\n    }\n    u, err := url.Parse(proxy)\n    if err != nil {\n        return fmt.Errorf(\"proxy %q unparseable: %w\", proxy, err)\n    }\n    if u.Scheme != \"http\" && u.Scheme != \"https\" && u.Scheme != \"socks5\" {\n        return fmt.Errorf(\"proxy scheme %q unsupported\", u.Scheme)\n    }\n    return nil\n}\n\nif err := validateProxy(cfg.Proxy); err != nil { return err }","typeGuard":"func isInvalidProxyURL(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"invalid proxy URL\")\n}","tryCatchPattern":"client, err := weixin.NewApiClient(baseURL, token, proxy)\nif err != nil {\n    if isInvalidProxyURL(err) {\n        // config bug: fix/strip the proxy string, never retry\n    }\n}","preventionTips":["Validate proxy URLs at config load, not at first API call","Percent-encode credentials in proxy URLs (% as %25)","Beware Windows line endings injecting \\r into env values","Use %q formatting to expose control characters when debugging"],"tags":["weixin","proxy","config","url-parsing"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}