larksuite/cli · error

HTTP %d: [%d] %s

Error message

HTTP %d: [%d] %s

What it means

This error means the Lark API returned an HTTP status >= 400 while fetching bot info, AND the response body parsed into a {code, msg} envelope with a non-zero Lark business code. The error surfaces both the HTTP status and the Lark error code/message so callers can see why bot authentication was rejected — typically an invalid or insufficient app access token — rather than just the bare HTTP code.

Source

Thrown at internal/identitydiag/diagnostics.go:422

	body, _ := io.ReadAll(resp.Body)
	// /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"`
	}
	parseErr := json.Unmarshal(body, &envelope)

	if resp.StatusCode >= 400 {
		// Lark error responses are usually `{code, msg}` envelopes even on
		// non-2xx — surface them when present so callers see why bot auth
		// was rejected, not just the bare HTTP code.
		if parseErr == nil && envelope.Code != 0 {
			return nil, fmt.Errorf("HTTP %d: [%d] %s", resp.StatusCode, envelope.Code, envelope.Msg)
		}
		return nil, fmt.Errorf("HTTP %d", resp.StatusCode)
	}
	if parseErr != nil {
		return nil, fmt.Errorf("parse response: %w", parseErr)
	}
	if envelope.Code != 0 {
		return nil, fmt.Errorf("[%d] %s", envelope.Code, envelope.Msg)
	}
	if envelope.Data.OpenID == "" {
		return nil, errors.New("open_id is empty")
	}
	return &botInfo{OpenID: envelope.Data.OpenID, AppName: envelope.Data.AppName}, nil
}

func fillTokenFields(id *Identity, token *larkauth.StoredUAToken) {
	id.TokenStatus = larkauth.TokenStatus(token)
	id.Scope = token.Scope

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Look up the Lark code in the message (e.g. [99991661]) — for invalid token errors, refresh credentials with the CLI auth/login flow.
  2. Verify the app has bot capability enabled and is published/available in the tenant.
  3. Check that the configured brand/endpoints match the tenant (feishu vs lark) where the app is registered.
  4. Re-run identity diagnostics after fixing auth to confirm the bot info fetch succeeds.
Defensive patterns

Strategy: try-catch

Try / catch

info, err := fetchBotInfo(ctx, f, cfg, token)
if err != nil {
    var codeErr interface{ Error() string }
    msg := err.Error()
    if strings.HasPrefix(msg, "HTTP ") && strings.Contains(msg, "[") {
        // e.g. HTTP 401: [99991661] invalid access token
        if strings.Contains(msg, "99991661") || strings.Contains(msg, "99991663") {
            return fmt.Errorf("token rejected, re-authenticate: %w", err)
        }
        return fmt.Errorf("bot auth rejected by Lark: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: fetchBotInfo GETs /open-apis/bot/v3/info; the server responds 4xx/5xx, the body parses as a Lark envelope, parseErr is nil and envelope.Code != 0. Reached from diagnoseBot or diagnoseExternalBot.

Common situations: Expired or invalid tenant_access_token/app_access_token (HTTP 400/401 with code 99991663/99991661); app not published or bot capability disabled (tenant has no bot); wrong brand/endpoint pointing at an environment where the app doesn't exist; rate limiting (429).

Related errors


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