larksuite/cli · error

fetch bot info: open_id is empty

Error message

fetch bot info: open_id is empty

What it means

The bot info response succeeded (code 0) but the bot object has an empty open_id, so no usable bot identity exists. The library treats bot identity as mandatory output of this endpoint; an empty open_id implies a malformed or semantically empty bot record.

Source

Thrown at shortcuts/common/runner.go:190

	}
	// /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 {
		return nil, errs.NewValidationError(errs.SubtypeFailedPrecondition, "OpenAPI requests are unavailable during dry-run")
	}
	if ctx.apiClientFunc != nil {
		return ctx.apiClientFunc()
	}
	return ctx.Factory.NewAPIClientWithConfig(ctx.Config)

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Enable the bot capability for the app and confirm a bot instance exists in the tenant, then retry
  2. If the endpoint legitimately returns no bot, stop relying on bot identity: pass the identity explicitly or use user-based identity flows
  3. File/report a schema issue if the API returns code 0 with an empty bot object unexpectedly

Example fix

// before
info, _ := ctx.BotInfo()
useId(info.OpenID) // "open_id is empty" upstream
// after
info, err := ctx.BotInfo()
if err != nil || info.OpenID == "" { return errs.NewValidationError(errs.SubtypeFailedPrecondition, "bot identity unavailable; pass --attendee explicitly") }
Defensive patterns

Strategy: validation

Validate before calling

info, err := ctx.BotInfo()
if err == nil && info.OpenID == "" {
	// treat as unavailable identity; require explicit ID
}

Type guard

func hasBotIdentity(info *common.BotInfo) bool { return info != nil && info.OpenID != "" }

Try / catch

info, err := ctx.BotInfo()
if err != nil || info == nil || info.OpenID == "" {
	return errs.NewValidationError(errs.SubtypeFailedPrecondition, "bot identity unavailable; provide the attendee/identity explicitly")
}

Prevention

When it happens

Trigger: envelope.Data.OpenID == "" after successful decode — e.g. app has no bot bound, the tenant stripped identity fields, or the endpoint returned an empty bot object.

Common situations: App created without adding the bot capability, so /bot/v3/info returns an empty bot; region/tenant configurations that omit open_id.

Related errors


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