larksuite/cli · error
fetch bot info: unmarshal: %w
Error message
fetch bot info: unmarshal: %w
What it means
The HTTP response body of /bot/v3/info could not be decoded into the expected {code, msg, bot:{open_id, app_name}} envelope. This means the gateway returned an unexpected payload (HTML error page, different schema, truncated body). Note the bot payload lives under "bot", not "data", per this API's older convention.
Source
Thrown at shortcuts/common/runner.go:184
})
if err != nil {
return nil, fmt.Errorf("fetch bot info: %w", err)
}
if resp.StatusCode >= 400 {
return nil, fmt.Errorf("fetch bot info: HTTP %d", resp.StatusCode)
}
// /open-apis/bot/v3/info returns `{code, msg, bot: {...}}` — the bot
// payload is under "bot", not "data" as the newer Lark API convention.
var envelope struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data struct {
OpenID string `json:"open_id"`
AppName string `json:"app_name"`
} `json:"bot"`
}
if err := json.Unmarshal(resp.RawBody, &envelope); err != nil {
return nil, fmt.Errorf("fetch bot info: unmarshal: %w", err)
}
if envelope.Code != 0 {
return nil, fmt.Errorf("fetch bot info: [%d] %s", envelope.Code, envelope.Msg)
}
if envelope.Data.OpenID == "" {
return nil, fmt.Errorf("fetch bot info: open_id is empty")
}
return &BotInfo{OpenID: envelope.Data.OpenID, AppName: envelope.Data.AppName}, nil
}
// Ctx returns the context.Context propagated from cmd.Context().
func (ctx *RuntimeContext) Ctx() context.Context { return ctx.ctx }
// getAPIClient returns the cached APIClient, creating it on first use.
// Thread-safe via sync.OnceValues (initialized in newRuntimeContext).
// Falls back to direct construction for test contexts that bypass newRuntimeContext.
func (ctx *RuntimeContext) getAPIClient() (*client.APIClient, error) {
if ctx.offline {View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Log/inspect resp.RawBody to see the actual payload and confirm it is the expected JSON envelope
- If a proxy/SSO layer intercepts requests, bypass or authenticate it so the real Lark API responds
- Update the CLI if the endpoint schema changed; verify against the current /bot/v3/info contract
Defensive patterns
Strategy: validation
Validate before calling
// Confirm the gateway returns JSON before parsing bot info:
resp, err := http.Get(baseURL + "/open-apis/bot/v3/info")
if err == nil && !strings.HasPrefix(strings.TrimSpace(http.DetectContentType(resp.Body[:512]), "application/json") { /* proxy/HTML interstitial */ } Try / catch
info, err := ctx.BotInfo()
if err != nil && strings.Contains(err.Error(), "unmarshal:") {
// unexpected payload: check for proxy/auth HTML, schema change
return fmt.Errorf("bot info endpoint returned non-JSON payload: %w", err)
} Prevention
- Bypass proxies/captive portals that inject HTML into API responses
- Pin the CLI to a gateway environment matching the expected /bot/v3/info schema
- Update the CLI when Lark changes the response envelope
When it happens
Trigger: json.Unmarshal(resp.RawBody, &envelope) fails — non-JSON body (proxy/auth HTML interstitial), a schema change in the endpoint response, or a corrupted/truncated response.
Common situations: Corporate proxy or captive portal returning HTML; API version/gateway changes altering the payload; hitting a mock or wrong environment returning unexpected JSON.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- ErrMalformedConfig
- protocol encode: %w
- protocol decode type: %w
- expected object, got %v
- expected string key, got %v
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/881bf2d47ab5528d.
Report an issue: GitHub.