chenhg5/cc-connect · critical

qqbot: app_secret is required

Error message

qqbot: app_secret is required

What it means

New() validates required credentials from the opts map before constructing the Platform. It type-asserts opts["app_id"] and opts["app_secret"] to string and returns this error when the app_secret key is absent, not a string, or an empty string. The QQ Bot API requires an app secret to exchange for access tokens, so construction without it is refused immediately.

Source

Thrown at platform/qqbot/qqbot.go:156

// msgTypeQuote indicates a quote (reply) message in the QQ Bot API.
const msgTypeQuote = 103

// msgElement represents a message element in QQ Bot event.
// For quote messages (message_type=103), msg_elements[0] contains the quoted content.
type msgElement struct {
	Content     string       `json:"content"`
	Attachments []attachment `json:"attachments"`
}

// New creates a new QQ Bot platform from config options.
func New(opts map[string]any) (core.Platform, error) {
	appID, _ := opts["app_id"].(string)
	if appID == "" {
		return nil, fmt.Errorf("qqbot: app_id is required")
	}
	appSecret, _ := opts["app_secret"].(string)
	if appSecret == "" {
		return nil, fmt.Errorf("qqbot: app_secret is required")
	}

	sandbox, _ := opts["sandbox"].(bool)
	allowFrom, _ := opts["allow_from"].(string)
	shareSessionInChannel, _ := opts["share_session_in_channel"].(bool)
	markdownSupport, _ := opts["markdown_support"].(bool)

	intents := defaultIntents
	if v, ok := opts["intents"].(int); ok && v > 0 {
		intents = v
	} else if v, ok := opts["intents"].(float64); ok && v > 0 {
		intents = int(v)
	}

	core.CheckAllowFrom("qqbot", allowFrom)
	dataDir, _ := opts["cc_data_dir"].(string)
	return &Platform{
		appID:                 appID,

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Set app_secret in the platform options as a plain string: opts["app_secret"] = "your-secret" in the config.toml [platforms.qqbot] section (or equivalent).
  2. Check the key spelling and casing — it must be exactly "app_secret".
  3. Ensure the value is a Go string, not []byte or another type; convert with string(b) if read from a file.
  4. Verify app_id is also set — New checks app_id first, so a missing app_id error can mask a missing secret until app_id is fixed.

Example fix

// before
opts := map[string]any{"app_id": "123"}
p, err := qqbot.New(opts) // qqbot: app_secret is required
// after
opts := map[string]any{"app_id": "123", "app_secret": os.Getenv("QQBOT_APP_SECRET")}
p, err := qqbot.New(opts)
Defensive patterns

Strategy: validation

Validate before calling

func validateQQBotOpts(opts map[string]any) error {
    id, _ := opts["app_id"].(string)
    sec, _ := opts["app_secret"].(string)
    if id == "" { return errors.New("qqbot opts: app_id missing or not a string") }
    if sec == "" { return errors.New("qqbot opts: app_secret missing or not a string") }
    return nil
}

Type guard

sec, ok := opts["app_secret"].(string); if !ok || sec == "" { /* refuse to construct */ }

Prevention

When it happens

Trigger: Calling qqbot.New(opts) where opts["app_secret"] is missing, is nil, or where its concrete type is not string (e.g. it was passed as []byte, a typed constant, or loaded from YAML/JSON as another type).

Common situations: Typo in the TOML/YAML key (appsecret, app-secret) so it never reaches opts; secret left out of an environment-driven config loader; secret declared as []byte from os.ReadFile; copying a Feishu/Telegram config block that has no app_secret field.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/109af947ae24dcfd. Report an issue: GitHub.