multica-ai/multica · error · ErrBindingAlreadyAssigned
slack: user id is already bound to a different user
Error message
slack: user id is already bound to a different user
What it means
Slack binding sentinel error: the Slack user id being bound is already bound to a different Multica user. As with lark, a binding token cannot transfer an already-bound Slack identity; account transfer must go through an explicit unbind flow.
Source
Thrown at server/internal/integrations/slack/binding.go:37
// This file is the Slack user-binding token flow: an unbound Slack user who
// messages the bot gets a "link your account" prompt (minted here, delivered by
// 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
}View on GitHub (pinned to 2c0912b6ec)
Solutions
- Sign in as the Multica user that currently owns the binding and unbind the Slack user id via the explicit unbind flow, then request a new binding link.
- Confirm the intended owner by looking up the existing channel user-binding row for the Slack user id before retrying.
- For shared Slack accounts, agree on a single owning Multica account instead of round-robining the binding.
Example fix
// before
res, err := svc.Redeem(ctx, rawToken)
// -> "slack: user id is already bound to a different user"
// after
res, err := svc.Redeem(ctx, rawToken)
if errors.Is(err, slack.ErrBindingAlreadyAssigned) {
renderUnbindRequired(w) // point user at the unbind flow
return
} Defensive patterns
Strategy: try-catch
Validate before calling
owner, err := store.GetChannelUserBinding(ctx, "slack", slackUserID)
if err == nil && owner != redeemingUserID {
return renderUnbindRequired(w)
}
_ = slackSvc.Redeem(ctx, rawToken) Try / catch
res, err := slackSvc.Redeem(ctx, rawToken)
if err != nil {
if errors.Is(err, slack.ErrBindingAlreadyAssigned) {
return respondConflict(w, "bound to another account — unbind first")
}
return err
} Prevention
- Bind one Slack user id to exactly one Multica account; use distinct Slack test users per test account.
- Expose the unbind flow next to the binding status so transfer never goes through a token.
- Include the Slack user id in error logs to locate the conflicting binding fast.
When it happens
Trigger: Redeeming a Slack binding token while a channel user-binding row already maps this Slack user id to another Multica user. Happens when the same person has two Multica accounts, or a Slack account is shared and one member already completed the link.
Common situations: User changed Multica accounts (left company A instance, joined with new account) and re-runs binding; shared/team Slack login already bound by a colleague; test environments reusing one Slack user against multiple accounts.
Related errors
- lark open_id is already bound to a different user
- slack: binding token invalid or expired
- wecom: user id is already bound to a different user
- slack: redeemer is not a workspace member
- slack installation not found
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/0fd8b7ac0e0fff1e.
Report an issue: GitHub.