{"record":{"id":"eecddfa3f21b1f24","repo":"sipeed/picoclaw","slug":"failed-to-create-api-client-w","errorCode":null,"errorMessage":"failed to create api client: %w","messagePattern":"failed to create api client: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/channels/weixin/auth.go","lineNumber":41,"sourceCode":"// It prints a QR code to the terminal for the user to scan.\n// Returns the BotToken, UserID, AccountID, and BaseUrl on success.\nfunc PerformLoginInteractive(\n\tctx context.Context,\n\topts AuthFlowOpts,\n) (botToken, userID, accountID, baseUrl string, err error) {\n\tif opts.BaseURL == \"\" {\n\t\topts.BaseURL = \"https://ilinkai.weixin.qq.com/\"\n\t}\n\tif opts.BotType == \"\" {\n\t\topts.BotType = \"3\" // Default iLink Bot Type\n\t}\n\tif opts.Timeout == 0 {\n\t\topts.Timeout = 5 * time.Minute\n\t}\n\n\tapi, err := NewApiClient(opts.BaseURL, \"\", opts.Proxy)\n\tif err != nil {\n\t\treturn \"\", \"\", \"\", \"\", fmt.Errorf(\"failed to create api client: %w\", err)\n\t}\n\tpollAPI := api\n\n\tlogger.InfoC(\"weixin\", \"Requesting Weixin QR code...\")\n\tqrResp, err := api.GetQRCode(ctx, opts.BotType)\n\tif err != nil {\n\t\treturn \"\", \"\", \"\", \"\", fmt.Errorf(\"failed to get qrcode: %w\", err)\n\t}\n\n\tfmt.Println(\"\\n=======================================================\")\n\tfmt.Println(\"Please scan the following QR code with WeChat to login:\")\n\tfmt.Println(\"=======================================================\")\n\tfmt.Println()\n\n\t// Create Small QR\n\tqrconfig := qrterminal.Config{\n\t\tLevel:      qrterminal.L,\n\t\tWriter:     os.Stdout,","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/weixin/auth.go#L23-L59","documentation":"The Weixin QR login flow could not even construct its API client, wrapping the error from NewApiClient. Given NewApiClient only fails on an unparseable proxy URL (error 666), this error almost always means opts.Proxy is malformed — the default BaseURL is filled in beforehand so it is never empty at this point. Nothing has touched the network yet when this is returned.","triggerScenarios":"Calling the interactive login entry point with opts.Proxy set to a string that fails url.Parse: control characters, NUL bytes, or an invalid percent-escape. BaseURL and BotType defaults are applied first (lines above), so those cannot cause it.","commonSituations":"Running headless login in CI with a proxy env var that contains a newline or unencoded credentials; a config template injecting a broken proxy value only in the login command path.","solutions":["Inspect opts.Proxy byte-for-byte (fmt %q) for hidden control characters","Percent-encode special characters in proxy credentials (% -> %25)","Test with the proxy setting empty to confirm login works without it, then fix the proxy value","Centralize proxy validation in config loading so bad values fail at startup with a clear message"],"exampleFix":"// before\n_, _, _, _, err := weixin.Login(ctx, weixin.LoginOpts{Proxy: rawProxy})\n\n// after\nif _, perr := url.Parse(rawProxy); perr != nil {\n    return fmt.Errorf(\"fix proxy config: %w\", perr)\n}\n_, _, _, _, err := weixin.Login(ctx, weixin.LoginOpts{Proxy: rawProxy})","handlingStrategy":"validation","validationCode":"if _, err := url.Parse(opts.Proxy); err != nil {\n    return fmt.Errorf(\"weixin login proxy misconfigured: %w\", err)\n}\n// only then start the interactive login flow","typeGuard":"func isWeixinClientCreateFail(err error) bool {\n    return err != nil && strings.HasPrefix(err.Error(), \"failed to create api client\")\n}","tryCatchPattern":"if _, _, _, _, err := weixin.Login(ctx, opts); err != nil {\n    if isWeixinClientCreateFail(err) {\n        // unwrap: almost always the proxy string; fix config, do not retry\n    }\n}","preventionTips":["Run the same proxy validation used for the runtime channel in the login command","Keep login and channel proxy config in one place so they cannot diverge","Log opts.Proxy with %q at login start to catch invisible characters early"],"tags":["weixin","auth","proxy","config","login"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}