sipeed/picoclaw · error
failed to create api client: %w
Error message
failed to create api client: %w
What it means
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.
Source
Thrown at pkg/channels/weixin/auth.go:41
// It prints a QR code to the terminal for the user to scan.
// Returns the BotToken, UserID, AccountID, and BaseUrl on success.
func PerformLoginInteractive(
ctx context.Context,
opts AuthFlowOpts,
) (botToken, userID, accountID, baseUrl string, err error) {
if opts.BaseURL == "" {
opts.BaseURL = "https://ilinkai.weixin.qq.com/"
}
if opts.BotType == "" {
opts.BotType = "3" // Default iLink Bot Type
}
if opts.Timeout == 0 {
opts.Timeout = 5 * time.Minute
}
api, err := NewApiClient(opts.BaseURL, "", opts.Proxy)
if err != nil {
return "", "", "", "", fmt.Errorf("failed to create api client: %w", err)
}
pollAPI := api
logger.InfoC("weixin", "Requesting Weixin QR code...")
qrResp, err := api.GetQRCode(ctx, opts.BotType)
if err != nil {
return "", "", "", "", fmt.Errorf("failed to get qrcode: %w", err)
}
fmt.Println("\n=======================================================")
fmt.Println("Please scan the following QR code with WeChat to login:")
fmt.Println("=======================================================")
fmt.Println()
// Create Small QR
qrconfig := qrterminal.Config{
Level: qrterminal.L,
Writer: os.Stdout,View on GitHub (pinned to 49183d7e8d)
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
Example fix
// before
_, _, _, _, err := weixin.Login(ctx, weixin.LoginOpts{Proxy: rawProxy})
// after
if _, perr := url.Parse(rawProxy); perr != nil {
return fmt.Errorf("fix proxy config: %w", perr)
}
_, _, _, _, err := weixin.Login(ctx, weixin.LoginOpts{Proxy: rawProxy}) Defensive patterns
Strategy: validation
Validate before calling
if _, err := url.Parse(opts.Proxy); err != nil {
return fmt.Errorf("weixin login proxy misconfigured: %w", err)
}
// only then start the interactive login flow Type guard
func isWeixinClientCreateFail(err error) bool {
return err != nil && strings.HasPrefix(err.Error(), "failed to create api client")
} Try / catch
if _, _, _, _, err := weixin.Login(ctx, opts); err != nil {
if isWeixinClientCreateFail(err) {
// unwrap: almost always the proxy string; fix config, do not retry
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- failed to load config: %w
- invalid proxy URL %q: %w
- failed to get qrcode: %w
- login timeout
- login confirmed but missing bot_token or ilink_bot_id
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/eecddfa3f21b1f24.
Report an issue: GitHub.