larksuite/cli · error

[%d] %s

Error message

[%d] %s

What it means

This error means the bot-info endpoint returned HTTP 2xx and the body parsed, but the Lark envelope carries a non-zero business code — the API-level failure signal. Unlike the >=400 case, this is a well-formed Lark error response on a success status, e.g. invalid token, permission denied, or app not found, with the authoritative Lark code and message.

Source

Thrown at internal/identitydiag/diagnostics.go:430

			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
	id.ExpiresAt = formatMillis(token.ExpiresAt)
	id.RefreshExpiresAt = formatMillis(token.RefreshExpiresAt)
	id.GrantedAt = formatMillis(token.GrantedAt)
}

func formatMillis(ms int64) string {
	if ms <= 0 {
		return ""

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Look up the Lark error code in the message ([code]) against Lark's error-code documentation to identify the exact auth/permission issue.
  2. For token errors, re-authenticate: run the CLI login/token refresh flow so a fresh app/tenant access token is issued.
  3. Verify the app has the bot capability and required scopes (e.g. contact/app info scopes) granted in the developer console.
  4. Re-run identity diagnostics to confirm the envelope code is 0 and bot info resolves.
Defensive patterns

Strategy: try-catch

Validate before calling

if token == nil || token.AccessToken == "" {
    return errors.New("no access token available; run auth login first")
}

Try / catch

info, err := fetchBotInfo(ctx, f, cfg, token)
if err != nil {
    msg := err.Error()
    if strings.HasPrefix(msg, "[") {
        switch {
        case strings.Contains(msg, "99991663"), strings.Contains(msg, "99991661"):
            return fmt.Errorf("invalid/expired token: %w", err) // trigger re-auth
        default:
            return fmt.Errorf("Lark API rejected bot info request: %w", err)
        }
    }
    return err
}

Prevention

When it happens

Trigger: fetchBotInfo receives a 2xx response, parses it successfully, and envelope.Code != 0. Reached from diagnoseBot or diagnoseExternalBot during identity diagnostics.

Common situations: App access token invalid or expired (code 99991663/99991661/99991668); app lacking required scopes/permissions for bot info; app not available in the tenant; invalid param errors from API-side validation.

Related errors


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