{"record":{"id":"f8f2007ebf3e1e5c","repo":"sipeed/picoclaw","slug":"bot-info-parse-w","errorCode":null,"errorMessage":"bot info parse: %w","messagePattern":"bot info parse: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/channels/feishu/feishu_64.go","lineNumber":726,"sourceCode":"// fetchBotOpenID calls the Feishu bot info API to retrieve and store the bot's open_id.\nfunc (c *FeishuChannel) fetchBotOpenID(ctx context.Context) error {\n\tresp, err := c.client.Do(ctx, &larkcore.ApiReq{\n\t\tHttpMethod:                http.MethodGet,\n\t\tApiPath:                   \"/open-apis/bot/v3/info\",\n\t\tSupportedAccessTokenTypes: []larkcore.AccessTokenType{larkcore.AccessTokenTypeTenant},\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"bot info request: %w\", err)\n\t}\n\n\tvar result struct {\n\t\tCode int `json:\"code\"`\n\t\tBot  struct {\n\t\t\tOpenID string `json:\"open_id\"`\n\t\t} `json:\"bot\"`\n\t}\n\tif err := json.Unmarshal(resp.RawBody, &result); err != nil {\n\t\treturn fmt.Errorf(\"bot info parse: %w\", err)\n\t}\n\tif result.Code != 0 {\n\t\tc.invalidateTokenOnAuthError(result.Code)\n\t\treturn fmt.Errorf(\"bot info api error (code=%d)\", result.Code)\n\t}\n\tif result.Bot.OpenID == \"\" {\n\t\treturn fmt.Errorf(\"bot info: empty open_id\")\n\t}\n\n\tc.botOpenID.Store(result.Bot.OpenID)\n\tlogger.InfoCF(\"feishu\", \"Fetched bot open_id from API\", map[string]any{\n\t\t\"open_id\": result.Bot.OpenID,\n\t})\n\treturn nil\n}\n\n// isBotMentioned checks if the bot was @mentioned in the message.\nfunc (c *FeishuChannel) isBotMentioned(message *larkim.EventMessage) bool {","sourceCodeStart":708,"sourceCodeEnd":744,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/feishu/feishu_64.go#L708-L744","documentation":"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.","triggerScenarios":"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.","commonSituations":"Corporate TLS-intercepting proxies; API gateway rewriting responses; Feishu API contract change after SDK version drift; response truncated by an aggressive middlebox.","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"],"exampleFix":"// before\nif err := json.Unmarshal(resp.RawBody, &result); err != nil {\n\treturn fmt.Errorf(\"bot info parse: %w\", err)\n}\n\n// after (diagnose what answered)\nif err := json.Unmarshal(resp.RawBody, &result); err != nil {\n\tpreview := len(resp.RawBody)\n\tif preview > 120 {\n\t\tpreview = 120\n\t}\n\tlogger.Warn(\"bot info payload not JSON\", \"len\", len(resp.RawBody), \"prefix\", string(resp.RawBody[:preview]))\n\treturn fmt.Errorf(\"bot info parse: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"func isBotInfoParseError(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"bot info parse\")\n}","tryCatchPattern":"if err := ch.Start(ctx); err == nil { /* Start swallows fetch errors */ }\n// if you call fetchBotOpenID-equivalent yourself:\nif err := fetchBotInfo(ctx); err != nil {\n\tif isBotInfoParseError(err) {\n\t\tlogger.Warn(\"bot info payload unparseable (proxy or API change?); mention detection degraded\", \"err\", err)\n\t\treturn nil // degrade, do not crash\n\t}\n\treturn err\n}","preventionTips":["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"],"tags":["feishu","json","parse","proxy","degraded"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}