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.ScopeView on GitHub (pinned to 7fd6ef3c07)
Solutions
- Look up the Lark code in the message (e.g. [99991661]) — for invalid token errors, refresh credentials with the CLI auth/login flow.
- Verify the app has bot capability enabled and is published/available in the tenant.
- Check that the configured brand/endpoints match the tenant (feishu vs lark) where the app is registered.
- 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
- Refresh app/tenant access tokens before they expire (schedule re-auth).
- Enable bot capability and publish the app in the tenant before running diagnostics.
- Match the CLI brand configuration (feishu vs lark) to where the app is registered.
- Monitor HTTP 429s and back off instead of hammering the endpoint.
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
- [%d] %s
- {json.dumps(envelope, ensure_ascii=False)}
- connection check: api error code=%d msg=%q
- HTTP %d
- fetch bot info: HTTP %d
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/ed5569daa8bbfd01.
Report an issue: GitHub.