multica-ai/multica · error · ErrTeamOwnedBySameWorkspace
slack: this Slack app is already connected to another agent
Error message
slack: this Slack app is already connected to another agent in this workspace
What it means
Slack install ownership error: the Slack app is already connected to a DIFFERENT live (non-archived) agent in the SAME workspace. Introduced (per #4810) because the old catch-all wrongly blamed 'another workspace'; naming the same-workspace case points the user at the Disconnect action they can actually reach.
Source
Thrown at server/internal/integrations/slack/install.go:40
// 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)
GetChannelInstallationOwnerByAppID(ctx context.Context, arg db.GetChannelInstallationOwnerByAppIDParams) (db.GetChannelInstallationOwnerByAppIDRow, error)
ListChannelInstallationsByWorkspace(ctx context.Context, arg db.ListChannelInstallationsByWorkspaceParams) ([]db.ChannelInstallation, error)
GetChannelInstallationInWorkspace(ctx context.Context, arg db.GetChannelInstallationInWorkspaceParams) (db.ChannelInstallation, error)
SetChannelInstallationStatus(ctx context.Context, arg db.SetChannelInstallationStatusParams) error
}View on GitHub (pinned to 2c0912b6ec)
Solutions
- Open the currently connected agent's settings in THIS workspace and disconnect its Slack app, then retry the connect for the new agent.
- If the wrong agent won the connection, disconnect from it and immediately connect to the intended agent (order matters).
- Check for accidentally duplicated agents; archive/delete the stray one after freeing the app.
Example fix
// before: raw error shown
// "slack: this Slack app is already connected to a different Multica workspace" (old catch-all, misleading for same-workspace case)
// after
if errors.Is(err, slack.ErrTeamOwnedBySameWorkspace) {
// same-workspace case: the fix is one click away
respond(w, 409, "already connected to another agent here — disconnect it first")
return
} Defensive patterns
Strategy: try-catch
Validate before calling
owner, err := queries.GetChannelInstallationOwnerByAppID(ctx, "slack", appID)
if err == nil && owner.WorkspaceID == thisWorkspace && owner.AgentID != params.AgentID {
return respondConflict(w, "disconnect it from its current agent in this workspace first")
}
_ = installSvc.Register(ctx, params) Try / catch
_, err := installSvc.Register(ctx, params)
if err != nil {
if errors.Is(err, slack.ErrTeamOwnedBySameWorkspace) {
// #4810: name the SAME-workspace case so the user finds the reachable Disconnect
return respondConflict(w, "already connected to another agent here — use Disconnect on that agent")
}
return err
} Prevention
- When reassigning a bot between agents, always Disconnect then Connect, in that order.
- Watch for duplicate agents created during setup; they silently hold connections.
- Handle this error separately from the different-workspace variant — the remediation differs.
When it happens
Trigger: Calling RegisterBYO to connect a Slack app to agent B while the same app id is live on agent A in the same workspace. The (channel_type, app_id) uniqueness forbids one Slack app feeding two agents.
Common situations: Admin swaps which agent owns the Slack bot and forgets to disconnect first; duplicate agent created during setup and the app wired to the wrong one; re-running the connect dialog against a second agent.
Related errors
- slack: this Slack app is already connected to a different Mu
- 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/99ef144126113f10.
Report an issue: GitHub.