sipeed/picoclaw · critical

feishu app_id or app_secret is empty

Error message

feishu app_id or app_secret is empty

What it means

FeishuChannel.Start refuses to boot when either config.AppID or the resolved config.AppSecret is an empty string. It is a fail-fast guard before any Feishu API traffic, including the bot open_id fetch and the event dispatcher setup. This is a configuration error and is never transient.

Source

Thrown at pkg/channels/feishu/feishu_64.go:92

	}
	ch := &FeishuChannel{
		BaseChannel: base,
		bc:          bc,
		config:      cfg,
		tokenCache:  tc,
		client:      lark.NewClient(cfg.AppID, cfg.AppSecret.String(), opts...),
	}
	ch.deleteMessageFn = ch.deleteMessageAPI
	ch.sendMediaPartFn = ch.sendMediaPart
	ch.sendTextFn = ch.sendText
	ch.progress = channels.NewToolFeedbackAnimator(ch.EditMessage)
	ch.SetOwner(ch)
	return ch, nil
}

func (c *FeishuChannel) Start(ctx context.Context) error {
	if c.config.AppID == "" || c.config.AppSecret.String() == "" {
		return fmt.Errorf("feishu app_id or app_secret is empty")
	}

	// Fetch bot open_id via API for reliable @mention detection.
	if err := c.fetchBotOpenID(ctx); err != nil {
		logger.ErrorCF("feishu", "Failed to fetch bot open_id, @mention detection may not work", map[string]any{
			"error": err.Error(),
		})
	}

	dispatcher := larkdispatcher.NewEventDispatcher(c.config.VerificationToken.String(), c.config.EncryptKey.String()).
		OnP2MessageReceiveV1(c.handleMessageReceive)

	runCtx, cancel := context.WithCancel(ctx)

	c.mu.Lock()
	c.cancel = cancel
	domain := lark.FeishuBaseUrl
	if c.config.IsLark {

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Set both app_id and app_secret in the feishu channel config (Feishu Open Platform 'Credentials & Basic Info' page)
  2. Verify the exact env var / config keys your loader reads and that the secret resolves non-empty (log len, never the value)
  3. Fail fast at process startup: validate config before starting the channel so the bot does not run half-alive
  4. If using a secret backend, check the mounted key path and permissions

Example fix

// before
ch, _ := feishu.NewFeishuChannel(cfg)
_ = ch.Start(ctx) // error surfaces late, bot silently down

// after
if cfg.AppID == "" || cfg.AppSecret.String() == "" {
	log.Fatal("feishu: app_id/app_secret not configured")
}
ch, err := feishu.NewFeishuChannel(cfg)
if err != nil {
	log.Fatal(err)
}
if err := ch.Start(ctx); err != nil {
	log.Fatal(err)
}
Defensive patterns

Strategy: validation

Validate before calling

func feishuConfigValid(cfg feishu.Config) error {
	if cfg.AppID == "" {
		return errors.New("feishu config: app_id is empty")
	}
	if cfg.AppSecret.String() == "" {
		return errors.New("feishu config: app_secret is empty")
	}
	return nil
}

// run BEFORE constructing/starting the channel
if err := feishuConfigValid(cfg); err != nil {
	log.Fatal(err)
}

Type guard

null

Try / catch

if err := ch.Start(ctx); err != nil {
	if strings.Contains(err.Error(), "app_id or app_secret is empty") {
		// config problem: stop the process, do not retry
		log.Fatal("fix feishu credentials: ", err)
	}
	return err
}

Prevention

When it happens

Trigger: NewFeishuChannel was built from a config whose AppID or AppSecret resolved to empty (unset env var, blank YAML field, secret loader returning an empty value), then Start(ctx) is called.

Common situations: Missing FEISHU_APP_ID/FEISHU_APP_SECRET in .env or deployment secrets; typo'd env var name so the config decodes to empty; secret manager path returns empty without failing; sample config copied without filling credentials.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/a4e175f1fdb61de1. Report an issue: GitHub.