Tencent/WeKnora · error

empty qqbot access token: code=%d message=%s

Error message

empty qqbot access token: code=%d message=%s

What it means

After a successful token request, the QQ bot API responded with an empty access_token. The client treats this as a hard failure and includes the API's code and message fields, which usually indicate credential rejection.

Source

Thrown at internal/im/qqbot/client.go:195

func (c *Client) AccessToken(ctx context.Context) (string, error) {
	c.mu.Lock()
	if c.accessToken != "" && time.Until(c.expiresAt) > time.Minute {
		token := c.accessToken
		c.mu.Unlock()
		return token, nil
	}
	c.mu.Unlock()

	body := map[string]string{
		"appId":        c.appID,
		"clientSecret": c.clientSecret,
	}
	var result tokenResponse
	if err := c.doJSON(ctx, http.MethodPost, appTokenURL, body, &result); err != nil {
		return "", err
	}
	if result.AccessToken == "" {
		return "", fmt.Errorf("empty qqbot access token: code=%d message=%s", result.Code, result.Message)
	}
	expiresIn := parseExpiresIn(result.ExpiresIn)

	c.mu.Lock()
	c.accessToken = result.AccessToken
	c.expiresAt = time.Now().Add(time.Duration(expiresIn) * time.Second)
	c.mu.Unlock()
	return result.AccessToken, nil
}

func parseExpiresIn(raw json.RawMessage) int {
	if len(raw) == 0 {
		return 7200
	}
	var number int
	if err := json.Unmarshal(raw, &number); err == nil && number > 0 {
		return number
	}

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Verify AppID and AppSecret in the channel credentials against the QQ developer console
  2. Read the code/message fields in the error — they state the API's rejection reason
  3. Confirm the app is approved/enabled and the correct api_base_url is used

Example fix

// before
creds := map[string]string{"appId": "", "clientSecret": ""}
// after
creds := map[string]string{"appId": "1020xxxxx", "clientSecret": "correct-secret-from-console"}
Defensive patterns

Strategy: validation

Validate before calling

if creds["appId"] == "" || creds["clientSecret"] == "" {
    return errors.New("qqbot appId and clientSecret are required before requesting a token")
}

Try / catch

token, err := client.AccessToken(ctx)
if err != nil {
    if strings.Contains(err.Error(), "empty qqbot access token") {
        return fmt.Errorf("check AppID/AppSecret in QQ console: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: AccessToken POSTs appId/clientSecret to the app token endpoint and the response struct has AccessToken == "" — typically because credentials are wrong, the app is disabled, or the API returned an error payload with 2xx/4xx that still decoded.

Common situations: Misconfigured QQBOT_APPID / QQBOT_APPSECRET; app not yet approved or sandbox restrictions; wrong api_base_url routing to a mock returning empty tokens; app secret rotated server-side.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/af2e2f0fa5509f81. Report an issue: GitHub.