sipeed/picoclaw · warning

bot info parse: %w

Error message

bot info parse: %w

What it means

The bot-info request returned a body that json.Unmarshal could not decode into the expected {code, bot.open_id} envelope. The request reached something, but the payload is not Feishu's JSON - typical of interception proxies returning HTML error pages, gateways injecting banners, or an API shape change. Start() logs it and continues with degraded mention detection.

Source

Thrown at pkg/channels/feishu/feishu_64.go:726

// fetchBotOpenID calls the Feishu bot info API to retrieve and store the bot's open_id.
func (c *FeishuChannel) fetchBotOpenID(ctx context.Context) error {
	resp, err := c.client.Do(ctx, &larkcore.ApiReq{
		HttpMethod:                http.MethodGet,
		ApiPath:                   "/open-apis/bot/v3/info",
		SupportedAccessTokenTypes: []larkcore.AccessTokenType{larkcore.AccessTokenTypeTenant},
	})
	if err != nil {
		return fmt.Errorf("bot info request: %w", err)
	}

	var result struct {
		Code int `json:"code"`
		Bot  struct {
			OpenID string `json:"open_id"`
		} `json:"bot"`
	}
	if err := json.Unmarshal(resp.RawBody, &result); err != nil {
		return fmt.Errorf("bot info parse: %w", err)
	}
	if result.Code != 0 {
		c.invalidateTokenOnAuthError(result.Code)
		return fmt.Errorf("bot info api error (code=%d)", result.Code)
	}
	if result.Bot.OpenID == "" {
		return fmt.Errorf("bot info: empty open_id")
	}

	c.botOpenID.Store(result.Bot.OpenID)
	logger.InfoCF("feishu", "Fetched bot open_id from API", map[string]any{
		"open_id": result.Bot.OpenID,
	})
	return nil
}

// isBotMentioned checks if the bot was @mentioned in the message.
func (c *FeishuChannel) isBotMentioned(message *larkim.EventMessage) bool {

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Capture and inspect resp.RawBody (log length/prefix) to identify what actually answered
  2. Bypass or configure the proxy for open.feishu.cn
  3. Pin the lark SDK version matching the current Feishu API; upgrade if the envelope changed
  4. Treat as non-fatal: the channel runs, mention detection is degraded

Example fix

// before
if err := json.Unmarshal(resp.RawBody, &result); err != nil {
	return fmt.Errorf("bot info parse: %w", err)
}

// after (diagnose what answered)
if err := json.Unmarshal(resp.RawBody, &result); err != nil {
	preview := len(resp.RawBody)
	if preview > 120 {
		preview = 120
	}
	logger.Warn("bot info payload not JSON", "len", len(resp.RawBody), "prefix", string(resp.RawBody[:preview]))
	return fmt.Errorf("bot info parse: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

func isBotInfoParseError(err error) bool {
	return err != nil && strings.Contains(err.Error(), "bot info parse")
}

Try / catch

if err := ch.Start(ctx); err == nil { /* Start swallows fetch errors */ }
// if you call fetchBotOpenID-equivalent yourself:
if err := fetchBotInfo(ctx); err != nil {
	if isBotInfoParseError(err) {
		logger.Warn("bot info payload unparseable (proxy or API change?); mention detection degraded", "err", err)
		return nil // degrade, do not crash
	}
	return err
}

Prevention

When it happens

Trigger: fetchBotOpenID receives resp.RawBody that is not the documented JSON: proxy/captive-portal HTML, an empty body, truncated response, or a renamed field breaking unmarshal.

Common situations: Corporate TLS-intercepting proxies; API gateway rewriting responses; Feishu API contract change after SDK version drift; response truncated by an aggressive middlebox.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/f8f2007ebf3e1e5c. Report an issue: GitHub.