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
- Capture and inspect resp.RawBody (log length/prefix) to identify what actually answered
- Bypass or configure the proxy for open.feishu.cn
- Pin the lark SDK version matching the current Feishu API; upgrade if the envelope changed
- 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
- Log a body-prefix on unmarshal failure to identify proxy/HTML interference fast
- Allowlist direct egress to open.feishu.cn, bypassing TLS-intercepting proxies
- Pin lark SDK versions and test against Feishu's current API in staging
- Treat parse failures as environmental drift - open a diagnostic, not a retry loop
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
- parse response: %w
- decode JSON response: %w
- failed to unmarshal response: %w
- parsing usage response: %w
- bot info request: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/f8f2007ebf3e1e5c.
Report an issue: GitHub.