multica-ai/multica · warning · ErrTeamOwnedByArchivedAgent
slack: this Slack app is connected to an archived agent in t
Error message
slack: this Slack app is connected to an archived agent in this workspace
What it means
Slack install ownership error: the Slack app's owning agent is ARCHIVED. Because archiving is reversible, the archived agent still holds the bot, so the app looks free in the agent list while it is not. Recovery is to restore that agent or disconnect its bot.
Source
Thrown at server/internal/integrations/slack/install.go:44
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
}
// dbInstallQueries adapts *db.Queries to installQueries — the generated WithTx
// returns *db.Queries, so we wrap it to return the interface (the same adapter
// pattern engine.ChatSession uses).View on GitHub (pinned to 2c0912b6ec)
Solutions
- Restore the archived agent, then either keep it as the owner or disconnect the Slack app from it and connect to the desired agent.
- If the agent should stay archived, restore it, disconnect the Slack app, and re-archive.
- Communicate in-team that archived agents keep their integrations — build a pre-archive checklist that includes disconnecting bots.
Example fix
// before
_, err := installSvc.Register(ctx, params)
// -> "slack: this Slack app is connected to an archived agent in this workspace"
// after: guide the admin to the restore path
if errors.Is(err, slack.ErrTeamOwnedByArchivedAgent) {
respond(w, 409, "this app is held by an archived agent — restore that agent, then disconnect the bot")
return
} Defensive patterns
Strategy: try-catch
Validate before calling
owner, err := queries.GetChannelInstallationOwnerByAppID(ctx, "slack", appID)
if err == nil && owner.AgentArchived {
return respondConflict(w, "restore the archived agent, then disconnect the bot")
}
_ = installSvc.Register(ctx, params) Try / catch
_, err := installSvc.Register(ctx, params)
if err != nil {
if errors.Is(err, slack.ErrTeamOwnedByArchivedAgent) {
return respondConflict(w, "app held by an archived agent — restore it or disconnect its bot")
}
return err
} Prevention
- Add 'disconnect integrations' to the pre-archive checklist for agents.
- When an app 'looks free' but install fails, check archived agents for hidden holders.
- Restore, disconnect, re-archive is the safe sequence for retiring an agent that owns a bot.
When it happens
Trigger: Calling RegisterBYO while the live channel_installation for the app id belongs to an agent with archived status — typical after 'temporarily' archiving an agent that owned the Slack connection.
Common situations: Team archives an old agent during a reorg without disconnecting its Slack bot; later admin sees no live agent holding the app and cannot understand the collision.
Related errors
- slack: this Slack app is already connected to a different Mu
- slack: this Slack app is already connected to another agent
- 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/61adc8b50c256801.
Report an issue: GitHub.