multica-ai/multica · error

wecom_credentials_rejected

wecom_credentials_rejected

Error message

wecom: WeCom rejected this bot id and secret

What it means

WeCom credential probe error: WeCom itself answered that the bot id + secret pair is invalid — wrong secret, a bot that no longer exists, or a bot whose API mode is off. Only WeCom global error codes on the documented rejection whitelist (document/path/90313) produce this; it is the answer an admin can act on.

Source

Thrown at server/internal/integrations/wecom/credential_probe.go:54

// refused, a rejected install has knocked a live bot offline for nothing.
// InstallationService.Upsert enforces this: it takes the slot's advisory lock,
// reads the current owner, and returns the conflict without probing for any
// live owner other than the caller's own row (see botSlotConflictErr). What
// reaches the probe is a free slot, a revoked row, an orphan, or a re-install
// of the caller's own bot — nothing anybody else is connected to.

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"log/slog"
)

// ErrCredentialsRejected is WeCom saying the pair is not valid: a wrong
// secret, a bot that no longer exists, a bot whose API mode is off. It is the
// answer that must reach the admin, because it is the one they can act on.
var ErrCredentialsRejected = errors.New("wecom: WeCom rejected this bot id and secret")

// ErrCredentialsUnverifiable is everything else — the dial failed, the
// handshake timed out, the network is down. Distinct from rejection on
// purpose: telling an admin their credentials are wrong when the deployment
// simply could not reach WeCom sends them to rotate a secret that was fine.
var ErrCredentialsUnverifiable = errors.New("wecom: could not reach WeCom to verify this bot")

// rejectionErrCodes are the WeCom global error codes (document/path/90313)
// documented as a refusal of the credential pair itself — the only answers
// entitled to tell an admin their Bot ID or secret is wrong.
//
// Everything else non-zero is ErrCredentialsUnverifiable, deliberately. WeCom
// only guarantees that 0 means success; the subscribe path is also under
// frequency and concurrency protection (45009, 45033), and the platform can
// fail on its own account. Reading any non-zero code as "wrong secret" pushes
// an admin to rotate a long-connection secret that was fine, and a rotated one
// cannot be recovered — the exact damage this file exists to prevent. So the
// list is a whitelist and the default is fail-closed: refuse the install, keep

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Re-copy both Bot ID and secret from the WeCom console (trim whitespace) and re-run the probe.
  2. Confirm the bot still exists and its API mode / long-connection capability is enabled in the WeCom admin console.
  3. If the secret was rotated, use the CURRENT value — old secrets stop working immediately after rotation.

Example fix

// before
err := probe.Check(ctx, botID, secret)
if err != nil {
	installFailed(w, err) // shows generic failure
}

// after
err := probe.Check(ctx, botID, secret)
switch {
case errors.Is(err, wecom.ErrCredentialsRejected):
	respond(w, 400, "WeCom rejected this bot id and secret — re-copy them and check API mode")
case errors.Is(err, wecom.ErrCredentialsUnverifiable):
	respond(w, 502, "could not reach WeCom — check network, do NOT rotate the secret")
}
Defensive patterns

Strategy: try-catch

Validate before calling

// cheap pre-check: trim inputs, confirm both are non-empty and well-formed
botID, secret = strings.TrimSpace(botID), strings.TrimSpace(secret)
if botID == "" || secret == "" {
	return respondFieldError(w, "", "both bot id and secret are required")
}

Try / catch

err := probe.Check(ctx, botID, secret)
if err != nil {
	switch {
	case errors.Is(err, wecom.ErrCredentialsRejected):
		// admin-actionable: re-copy credentials / enable API mode; do not retry
		return respondBadRequest(w, "WeCom rejected this bot id and secret")
	case errors.Is(err, wecom.ErrCredentialsUnverifiable):
		return respondBadGateway(w, "could not reach WeCom")
	}
	return err
}

Prevention

When it happens

Trigger: Calling the credential probe during install with a secret that was rotated, a typo'd Bot ID, a deleted bot, or a bot for which the long-connection API mode was never enabled. WeCom returns a whitelisted rejection code, which the probe maps to ErrCredentialsRejected.

Common situations: Secret regenerated in the WeCom console after it was pasted; copied bot id vs secret swapped; admin created the bot but did not enable API mode; trailing whitespace in either field.

Related errors


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/64494cc0238e187e. Report an issue: GitHub.