{"record":{"id":"99ef144126113f10","repo":"multica-ai/multica","slug":"slack-this-slack-app-is-already-connected-to-anot","errorCode":null,"errorMessage":"slack: this Slack app is already connected to another agent in this workspace","messagePattern":"slack: this Slack app is already connected to another agent in this workspace","errorType":"http","errorClass":"ErrTeamOwnedBySameWorkspace","httpStatus":409,"severity":"error","filePath":"server/internal/integrations/slack/install.go","lineNumber":40,"sourceCode":"// app-level token (xapp-) into Multica (the paste path lives in byo_install.go).\n// The InstallService owns the at-rest encryption of those tokens — so no caller\n// can write a channel_installation with a plaintext token — plus the shared\n// persistInstall transaction and the list / get / revoke management surface.\n\nvar (\n\t// ErrInstallationNotFound surfaces \"no row matches in this workspace\".\n\tErrInstallationNotFound = errors.New(\"slack installation not found\")\n\t// ErrTeamOwnedByAnotherWorkspace is returned when the pasted Slack app is\n\t// already connected to a live owner in a DIFFERENT Multica workspace — it\n\t// would collide with the (channel_type, app_id) routing index. A Slack app is\n\t// one bot identity and maps to one agent; reusing it here requires\n\t// disconnecting it in the other workspace first.\n\tErrTeamOwnedByAnotherWorkspace = errors.New(\"slack: this Slack app is already connected to a different Multica workspace\")\n\t// ErrTeamOwnedBySameWorkspace is returned when the app is already connected to\n\t// a DIFFERENT (live, non-archived) agent in the SAME workspace. The old\n\t// catch-all wrongly blamed \"another workspace\"; naming the same-workspace case\n\t// points the user at the Disconnect they can actually reach (#4810).\n\tErrTeamOwnedBySameWorkspace = errors.New(\"slack: this Slack app is already connected to another agent in this workspace\")\n\t// ErrTeamOwnedByArchivedAgent is returned when the app's owning agent is\n\t// archived (and so still holds the bot, since archiving is reversible). The\n\t// user recovers by restoring that agent or disconnecting its bot.\n\tErrTeamOwnedByArchivedAgent = errors.New(\"slack: this Slack app is connected to an archived agent in this workspace\")\n)\n\n// installQueries is the slice of generated queries InstallService needs. WithTx\n// returns the same interface bound to a transaction so persistInstall runs its\n// upsert atomically (and so tests can inject a fake without a real DB).\ntype installQueries interface {\n\tWithTx(tx pgx.Tx) installQueries\n\tUpsertChannelInstallation(ctx context.Context, arg db.UpsertChannelInstallationParams) (db.ChannelInstallation, error)\n\tReclaimDeadChannelInstallationByAppID(ctx context.Context, arg db.ReclaimDeadChannelInstallationByAppIDParams) (pgtype.UUID, error)\n\tGetChannelInstallationOwnerByAppID(ctx context.Context, arg db.GetChannelInstallationOwnerByAppIDParams) (db.GetChannelInstallationOwnerByAppIDRow, error)\n\tListChannelInstallationsByWorkspace(ctx context.Context, arg db.ListChannelInstallationsByWorkspaceParams) ([]db.ChannelInstallation, error)\n\tGetChannelInstallationInWorkspace(ctx context.Context, arg db.GetChannelInstallationInWorkspaceParams) (db.ChannelInstallation, error)\n\tSetChannelInstallationStatus(ctx context.Context, arg db.SetChannelInstallationStatusParams) error\n}","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/internal/integrations/slack/install.go#L22-L58","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: raw error shown\n// \"slack: this Slack app is already connected to a different Multica workspace\" (old catch-all, misleading for same-workspace case)\n\n// after\nif errors.Is(err, slack.ErrTeamOwnedBySameWorkspace) {\n\t// same-workspace case: the fix is one click away\n\trespond(w, 409, \"already connected to another agent here — disconnect it first\")\n\treturn\n}","handlingStrategy":"try-catch","validationCode":"owner, err := queries.GetChannelInstallationOwnerByAppID(ctx, \"slack\", appID)\nif err == nil && owner.WorkspaceID == thisWorkspace && owner.AgentID != params.AgentID {\n\treturn respondConflict(w, \"disconnect it from its current agent in this workspace first\")\n}\n_ = installSvc.Register(ctx, params)","typeGuard":null,"tryCatchPattern":"_, err := installSvc.Register(ctx, params)\nif err != nil {\n\tif errors.Is(err, slack.ErrTeamOwnedBySameWorkspace) {\n\t\t// #4810: name the SAME-workspace case so the user finds the reachable Disconnect\n\t\treturn respondConflict(w, \"already connected to another agent here — use Disconnect on that agent\")\n\t}\n\treturn err\n}","preventionTips":["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."],"tags":["slack","byo","conflict","same-workspace","integration","go"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}