multica-ai/multica · error · ErrBindingNotWorkspaceMember
slack: redeemer is not a workspace member
Error message
slack: redeemer is not a workspace member
What it means
Slack binding sentinel error: the user redeeming the binding token is not a member of the token's workspace. Detected by an explicit membership check during redemption; the HTTP boundary maps it to 403.
Source
Thrown at server/internal/integrations/slack/binding.go:40
// the OutboundReplier), clicks through to the in-product redeem page, and their
// Slack user id is bound to their Multica account. It mirrors
// lark.BindingTokenService but runs on the generic channel_* queries with
// channel_type='slack' (lark's ChannelStore hardcodes 'feishu').
// BindingTokenTTL bounds a token's life. The channel_binding_token CHECK
// enforces the same 15-minute cap so a misconfigured caller cannot mint longer.
const BindingTokenTTL = 15 * time.Minute
var (
// ErrBindingTokenInvalid: token unknown / already consumed / expired. One
// opaque error for all three avoids a replay timing oracle.
ErrBindingTokenInvalid = errors.New("slack: binding token invalid or expired")
// ErrBindingAlreadyAssigned: this Slack user id is already bound to a
// different Multica user (account transfer must go through explicit unbind).
ErrBindingAlreadyAssigned = errors.New("slack: user id is already bound to a different user")
// ErrBindingNotWorkspaceMember: the redeemer is not a member of the token's
// workspace. Translated to 403 at the HTTP boundary.
ErrBindingNotWorkspaceMember = errors.New("slack: redeemer is not a workspace member")
)
// BindingToken is a freshly minted token. The raw value is returned exactly
// once (embedded in the binding URL); only its hash is persisted.
type BindingToken struct {
Raw string
ExpiresAt time.Time
}
// RedeemedBindingToken is returned after a successful redemption.
type RedeemedBindingToken struct {
WorkspaceID pgtype.UUID
InstallationID pgtype.UUID
SlackUserID string
}
// BindingTokenService mints and redeems Slack binding tokens. Redemption is
// transactional: consuming the token and inserting the channel_user_binding rowView on GitHub (pinned to 2c0912b6ec)
Solutions
- Have a workspace admin re-add the redeeming user, then request a fresh binding link (the old one may have expired).
- Make sure the redeem page session matches a user account that is a member of the token's workspace.
- Check membership rows for the (workspace, user) pair if the user believes they are a member — stale session data can mask a removal.
Example fix
// before
res, err := svc.Redeem(ctx, rawToken)
if err != nil {
http.Error(w, err.Error(), 500)
}
// after
res, err := svc.Redeem(ctx, rawToken)
if errors.Is(err, slack.ErrBindingNotWorkspaceMember) {
http.Error(w, "you must be a member of this workspace", http.StatusForbidden)
return
} Defensive patterns
Strategy: validation
Validate before calling
if !members.IsMember(ctx, token.WorkspaceID, user.ID) {
return respondForbidden(w, "join the workspace before linking Slack")
}
_ = slackSvc.Redeem(ctx, rawToken) Try / catch
if err := slackSvc.Redeem(ctx, rawToken); err != nil {
if errors.Is(err, slack.ErrBindingNotWorkspaceMember) {
return respondStatus(w, http.StatusForbidden)
}
return err
} Prevention
- Re-validate membership at redemption time, not just at link-mint time.
- Show which workspace a binding link belongs to before the user commits.
- Treat removal-from-workspace during a binding flow as an expected race with a friendly 403.
When it happens
Trigger: Redeeming a Slack binding token when the redeemer has no membership row in the workspace the token was minted for — e.g. removed from the workspace mid-flow, or signed into a different Multica account that never belonged to that workspace.
Common situations: Admin kicks the user during the token's 15-minute life and the user then clicks the DM link; multi-workspace users redeeming a link while the wrong workspace/account is active in the session.
Related errors
- redeemer is not a workspace member
- wecom: redeemer is not a workspace member
- slack: binding token invalid or expired
- slack: user id is already bound to a different user
- Invalid desktop runtime config JSON: ${err instanceof Error
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/9c4699cd4ee95d6f.
Report an issue: GitHub.