multica-ai/multica · error

wecom_bot_owned_by_same_workspace

wecom_bot_owned_by_same_workspace

Error message

wecom: this bot is already connected to another agent in this workspace

What it means

WeCom install ownership error: another agent in the SAME workspace already holds this bot. The UpsertChannelInstallation ON CONFLICT targets (workspace_id, agent_id, channel_type), but the UNIQUE index on (channel_type, config->>'app_id') makes a second agent trip the index instead; this sentinel replaces the raw Postgres duplicate-key text with a message naming the reachable fix. One bot = one live long-connection subscriber, so it cannot be shared.

Source

Thrown at server/internal/integrations/wecom/installation.go:302

	}
	return Installation{}, nil
}

// Sentinels for the one conflict Upsert cannot resolve: the bot is already
// connected somewhere else. UpsertChannelInstallation conflicts on
// (workspace_id, agent_id, channel_type), but idx_channel_installation_type_appid
// is UNIQUE on (channel_type, config->>'app_id') — so connecting the SAME bot to
// a second agent misses the ON CONFLICT clause entirely and trips the index
// instead. Without these the admin reads the raw Postgres text
// ("duplicate key value violates unique constraint …") in a toast.
//
// One bot is one connection: the WeCom long connection allows a single
// live subscriber per bot, so two agents cannot share one. The way out is
// always to free the bot first, which is what each message says.
var (
	// ErrBotOwnedBySameWorkspace — another agent in the admin's own workspace
	// holds the bot. Reversible from the same settings screen.
	ErrBotOwnedBySameWorkspace = errors.New("wecom: this bot is already connected to another agent in this workspace")

	// ErrBotOwnedByArchivedAgent — the holder is archived, so it does not show
	// up in the agent list and the bot looks free while it is not.
	ErrBotOwnedByArchivedAgent = errors.New("wecom: this bot is connected to an archived agent in this workspace")

	// ErrBotOwnedByAnotherWorkspace — the holder is out of sight entirely and
	// only someone with access there can release it.
	ErrBotOwnedByAnotherWorkspace = errors.New("wecom: this bot is already connected to a different Multica workspace")
)

// pgUniqueViolation is Postgres' unique_violation SQLSTATE.
const pgUniqueViolation = "23505"

// botSlotConflictErr answers one question: may this install act on the
// (wecom, bot_id) routing slot at all? It returns the sentinel to refuse with,
// or nil to proceed. MUST be called inside the transaction holding
// LockChannelInstallationAppIDSlot — outside it the answer is a guess that a
// concurrent install or reconnect can invalidate before it is used.

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Disconnect the bot from the other agent in this workspace (settings screen), then connect it to the intended agent.
  2. If unsure who holds it, list installations for the workspace and find the (channel_type='wecom', same app_id) row.
  3. For genuinely shared needs, connect each agent to a different WeCom bot.

Example fix

// before: raw Postgres error reaches the admin
// "duplicate key value violates unique constraint idx_channel_installation_type_appid"

// after: service maps 23505 to actionable sentinels
if errors.Is(err, wecom.ErrBotOwnedBySameWorkspace) {
	respond(w, 409, "free the bot first: disconnect it from the other agent in this workspace")
	return
}
Defensive patterns

Strategy: try-catch

Validate before calling

owner, err := queries.GetChannelInstallationOwnerByAppID(ctx, "wecom", appID)
if err == nil && owner.WorkspaceID == thisWorkspace && owner.AgentID != params.AgentID {
	return respondConflict(w, "disconnect the bot from its current agent first")
}
_ = installSvc.Install(ctx, params)

Try / catch

if err := installSvc.Install(ctx, params); err != nil {
	if errors.Is(err, wecom.ErrBotOwnedBySameWorkspace) {
		// 23505 on (channel_type, app_id) mapped to an actionable message
		return respondConflict(w, "free the bot: disconnect it from the other agent in this workspace")
	}
	return err
}

Prevention

When it happens

Trigger: Connecting a WeCom bot to agent B while agent A in the same workspace already has it installed — the upsert misses the ON CONFLICT clause and raises unique_violation (SQLSTATE 23505) on idx_channel_installation_type_appid, which the service translates to this error.

Common situations: Reassigning which agent fronts a WeCom bot without disconnecting first; duplicated agents during setup; admin assumes bots are shareable across agents.

Related errors


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/fb83e3827cd9a858. Report an issue: GitHub.