microsoft/semantic-kernel · error · InvalidOperationException

Subscription with id {subscription.Id} already exists.

Error message

Subscription with id {subscription.Id} already exists.

What it means

AddSubscriptionAsync refuses duplicates keyed by subscription.Id and throws InvalidOperationException. Subscription IDs default to a new GUID, so collisions are rare unless you pass an explicit `id`. The dictionary is keyed solely by Id, so two distinct subscriptions sharing an Id conflict even with different topic/agent mappings.

Source

Thrown at dotnet/src/Agents/Runtime/InProcess/InProcessRuntime.cs:205

    public async ValueTask LoadAgentStateAsync(AgentId agentId, JsonElement state)
    {
        IHostableAgent agent = await this.EnsureAgentAsync(agentId).ConfigureAwait(false);
        await agent.LoadStateAsync(state).ConfigureAwait(false);
    }

    /// <inheritdoc/>
    public async ValueTask<JsonElement> SaveAgentStateAsync(AgentId agentId)
    {
        IHostableAgent agent = await this.EnsureAgentAsync(agentId).ConfigureAwait(false);
        return await agent.SaveStateAsync().ConfigureAwait(false);
    }

    /// <inheritdoc/>
    public ValueTask AddSubscriptionAsync(ISubscriptionDefinition subscription)
    {
        if (this._subscriptions.ContainsKey(subscription.Id))
        {
            throw new InvalidOperationException($"Subscription with id {subscription.Id} already exists.");
        }

        this._subscriptions.Add(subscription.Id, subscription);

#if !NETCOREAPP
        return Task.CompletedTask.AsValueTask();
#else
        return ValueTask.CompletedTask;
#endif
    }

    /// <inheritdoc/>
    public ValueTask RemoveSubscriptionAsync(string subscriptionId)
    {
        if (!this._subscriptions.ContainsKey(subscriptionId))
        {
            throw new InvalidOperationException($"Subscription with id {subscriptionId} does not exist.");
        }

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Generate a fresh subscription (omit `id`) or use RemoveSubscriptionAsync before re-adding.
  2. Make registration idempotent by checking/looping existing subscriptions before AddSubscriptionAsync.
  3. Use a new InProcessRuntime instance per independent run.

Example fix

// before
await runtime.AddSubscriptionAsync(new TypeSubscription("t1", "a1", id: "fixed-id"));
await runtime.AddSubscriptionAsync(new TypeSubscription("t2", "a2", id: "fixed-id")); // throws

// after
await runtime.AddSubscriptionAsync(new TypeSubscription("t1", "a1", id: "fixed-id"));
await runtime.AddSubscriptionAsync(new TypeSubscription("t2", "a2")); // unique GUID id
Defensive patterns

Strategy: validation

Validate before calling

// No public enumeration of IDs; track added IDs yourself.
HashSet<string> added = new();
async ValueTask AddOnceAsync(InProcessRuntime rt, ISubscriptionDefinition sub)
{
    if (added.Add(sub.Id)) await rt.AddSubscriptionAsync(sub);
}

Try / catch

try { await runtime.AddSubscriptionAsync(sub); }
catch (InvalidOperationException) { /* duplicate id; already registered */ }

Prevention

When it happens

Trigger: Registering a subscription with an explicit `id` that was already added; re-running registration code (e.g. in a retry or re-init) without clearing subscriptions; reusing a subscription object whose Id was already added.

Common situations: Deterministic subscription IDs in config-driven setup that get re-applied; test setup not resetting the runtime; idempotent init code that runs twice.

Related errors


AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13). Data as JSON: /api/errors/c1eb1919f2ddc213. Report an issue: GitHub.