sipeed/picoclaw · critical

wecom bot_id and secret are required

Error message

wecom bot_id and secret are required

What it means

WeComChannel construction fails fast when either BotID or Secret is empty in the WeCom settings. These are the minimum credentials the channel needs to authenticate with the WeCom gateway, so NewChannel refuses to build rather than starting a channel that would fail every command. This is a configuration error at startup, not a runtime send failure.

Source

Thrown at pkg/channels/wecom/wecom.go:113

		return true
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	if _, ok := s.seen[id]; ok {
		return false
	}
	if old := s.ring[s.idx]; old != "" {
		delete(s.seen, old)
	}
	s.ring[s.idx] = id
	s.idx = (s.idx + 1) % len(s.ring)
	s.seen[id] = struct{}{}
	return true
}

func NewChannel(bc *config.Channel, cfg *config.WeComSettings, messageBus *bus.MessageBus) (*WeComChannel, error) {
	if cfg.BotID == "" || cfg.Secret.String() == "" {
		return nil, fmt.Errorf("wecom bot_id and secret are required")
	}
	if cfg.WebSocketURL == "" {
		cfg.WebSocketURL = wecomDefaultWebSocketURL
	}

	base := channels.NewBaseChannel(
		"wecom",
		cfg,
		messageBus,
		bc.AllowFrom,
		channels.WithReasoningChannelID(bc.ReasoningChannelID),
	)

	ch := &WeComChannel{
		BaseChannel: base,
		config:      cfg,
		pending:     make(map[string]chan wecomEnvelope),
		turns:       make(map[string][]wecomTurn),

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Set both bot_id and secret in the channels.wecom settings block of the config file
  2. If values come from environment variables, verify they are actually exported and non-empty in the deployment environment (print the resolved config with the secret masked)
  3. Check for typos/renamed keys in the config against the WeComSettings struct definition
  4. Restart the service after fixing the config; the error occurs only at channel construction

Example fix

# before (config.yaml)
channels:
  wecom:
    bot_id: ""
    # secret missing entirely

# after
channels:
  wecom:
    bot_id: "wecom-bot-123"
    secret: "${WECOM_SECRET}"  # exported and non-empty
Defensive patterns

Strategy: validation

Validate before calling

func validateWeComConfig(cfg *config.WeComSettings) error {
    if cfg.BotID == "" {
        return errors.New("channels.wecom.bot_id is required")
    }
    if cfg.Secret.String() == "" {
        return errors.New("channels.wecom.secret is required")
    }
    return nil
}

// run before constructing the channel / starting the app
if err := validateWeComConfig(cfg); err != nil {
    return err
}

Type guard

func isWeComMissingCredentials(err error) bool {
    return err != nil && strings.Contains(err.Error(), "bot_id and secret are required")
}

Try / catch

ch, err := wecom.NewChannel(bc, cfg, bus)
if err != nil {
    if isWeComMissingCredentials(err) {
        // fail startup loudly; do not run a degraded channel
        log.Fatalf("config error: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: Instantiating the WeCom channel (via the channel manager during app bootstrap) with a config where channels.wecom.bot_id or channels.wecom.secret is missing or an empty string; Secret is a masked type, so it must have been set and .String() must return a non-empty value.

Common situations: Deploying with an incomplete YAML/JSON config (secret omitted in prod but present in dev); env-var interpolation producing an empty string (e.g. WECOM_SECRET not exported); renaming config keys in a template so the values never bind; copying an example config without filling in credentials.

Related errors


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