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

  1. Log/inspect resp.RawBody to see the actual payload and confirm it is the expected JSON envelope
  2. If a proxy/SSO layer intercepts requests, bypass or authenticate it so the real Lark API responds
  3. 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

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


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/881bf2d47ab5528d. Report an issue: GitHub.