multica-ai/multica · error · ErrTeamOwnedByAnotherWorkspace
slack: this Slack app is already connected to a different Mu
Error message
slack: this Slack app is already connected to a different Multica workspace
What it means
Slack install ownership error: the pasted Slack app (identified by app id) is already connected to a live owner in a DIFFERENT Multica workspace, which would collide with the (channel_type, app_id) routing index. A Slack app is one bot identity mapped to one agent, so it must be disconnected in the other workspace first.
Source
Thrown at server/internal/integrations/slack/install.go:35
)
// This file is the Slack install backend (MUL-3666). Slack uses the
// bring-your-own-app (BYO) model: the workspace admin creates their own Slack
// app, installs it to their Slack workspace, and pastes its bot token (xoxb-) +
// app-level token (xapp-) into Multica (the paste path lives in byo_install.go).
// The InstallService owns the at-rest encryption of those tokens — so no caller
// can write a channel_installation with a plaintext token — plus the shared
// persistInstall transaction and the list / get / revoke management surface.
var (
// ErrInstallationNotFound surfaces "no row matches in this workspace".
ErrInstallationNotFound = errors.New("slack installation not found")
// ErrTeamOwnedByAnotherWorkspace is returned when the pasted Slack app is
// already connected to a live owner in a DIFFERENT Multica workspace — it
// would collide with the (channel_type, app_id) routing index. A Slack app is
// one bot identity and maps to one agent; reusing it here requires
// disconnecting it in the other workspace first.
ErrTeamOwnedByAnotherWorkspace = errors.New("slack: this Slack app is already connected to a different Multica workspace")
// ErrTeamOwnedBySameWorkspace is returned when the app is already connected to
// a DIFFERENT (live, non-archived) agent in the SAME workspace. The old
// catch-all wrongly blamed "another workspace"; naming the same-workspace case
// points the user at the Disconnect they can actually reach (#4810).
ErrTeamOwnedBySameWorkspace = errors.New("slack: this Slack app is already connected to another agent in this workspace")
// ErrTeamOwnedByArchivedAgent is returned when the app's owning agent is
// archived (and so still holds the bot, since archiving is reversible). The
// user recovers by restoring that agent or disconnecting its bot.
ErrTeamOwnedByArchivedAgent = errors.New("slack: this Slack app is connected to an archived agent in this workspace")
)
// installQueries is the slice of generated queries InstallService needs. WithTx
// returns the same interface bound to a transaction so persistInstall runs its
// upsert atomically (and so tests can inject a fake without a real DB).
type installQueries interface {
WithTx(tx pgx.Tx) installQueries
UpsertChannelInstallation(ctx context.Context, arg db.UpsertChannelInstallationParams) (db.ChannelInstallation, error)
ReclaimDeadChannelInstallationByAppID(ctx context.Context, arg db.ReclaimDeadChannelInstallationByAppIDParams) (pgtype.UUID, error)View on GitHub (pinned to 2c0912b6ec)
Solutions
- In the OTHER Multica workspace that owns the app, run Disconnect on that installation to release it.
- If the other workspace is unreachable (org split, deleted tenant), have an operator revoke/remove the owning channel_installation row directly.
- Alternatively connect a DIFFERENT Slack app (new xoxb-/xapp- pair) to this workspace instead of sharing one.
Example fix
// before
_, err := installSvc.Register(ctx, params)
if err != nil {
return err // surfaces raw to admin
}
// after
_, err := installSvc.Register(ctx, params)
switch {
case errors.Is(err, slack.ErrTeamOwnedByAnotherWorkspace):
respond(w, 409, "disconnect this Slack app in its other workspace first")
case errors.Is(err, slack.ErrTeamOwnedBySameWorkspace):
respond(w, 409, "disconnect it from the other agent in this workspace (Agents > Disconnect)")
} Defensive patterns
Strategy: try-catch
Validate before calling
owner, err := queries.GetChannelInstallationOwnerByAppID(ctx, "slack", appID)
if err == nil && owner.WorkspaceID != thisWorkspace {
return respondConflict(w, "disconnect this app in its other workspace first")
}
_ = installSvc.Register(ctx, params) Try / catch
_, err := installSvc.Register(ctx, params)
if err != nil {
if errors.Is(err, slack.ErrTeamOwnedByAnotherWorkspace) {
return respondConflict(w, "owned by another workspace — disconnect it there first")
}
return err
} Prevention
- Use one Slack app per Multica workspace; never share apps across instances.
- Always run Disconnect before decommissioning or migrating a workspace.
- Document the (channel_type, app_id) uniqueness rule for admins connecting apps.
When it happens
Trigger: Calling RegisterBYO/persistInstall while a live (non-revoked) channel_installation for the same app id exists under another workspace's agent — e.g. the same Slack app was previously wired into a second Multica workspace or a leftover instance.
Common situations: Trialing in a sandbox workspace then moving to production without disconnecting; two teams independently connecting the same Slack app; dev/staging/prod Multica instances sharing one Slack app.
Related errors
- slack: this Slack app is already connected to another agent
- slack: this Slack app is connected to an archived agent in t
- slack: binding token invalid or expired
- slack: user id is already bound to a different user
- slack: bot token must start with xoxb-
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/35d7f2c5f8d48e9b.
Report an issue: GitHub.