microsoft/semantic-kernel · error · InvalidOperationException

TopicId does not match the subscription.

Error message

TopicId does not match the subscription.

What it means

TypePrefixSubscription.MapToAgent checks Matches(topic) (topic.Type.StartsWith(TopicTypePrefix, Ordinal)) and throws InvalidOperationException if it does not. The XML doc explicitly states MapToAgent should only be called after Matches returns true. The runtime always calls Matches before MapToAgent, so reaching this throw indicates a logic error or direct misuse.

Source

Thrown at dotnet/src/Agents/Runtime/Core/TypePrefixSubscription.cs:72

    /// </summary>
    /// <param name="topic">The topic to check.</param>
    /// <returns><c>true</c> if the topic's type starts with the subscription's prefix, <c>false</c> otherwise.</returns>
    public bool Matches(TopicId topic)
    {
        return topic.Type.StartsWith(this.TopicTypePrefix, StringComparison.Ordinal);
    }

    /// <summary>
    /// Maps a <see cref="TopicId"/> to an <see cref="AgentId"/>. Should only be called if <see cref="Matches"/> returns true.
    /// </summary>
    /// <param name="topic">The topic to map.</param>
    /// <returns>An <see cref="AgentId"/> representing the agent that should handle the topic.</returns>
    /// <exception cref="InvalidOperationException">Thrown if the topic does not match the subscription.</exception>
    public AgentId MapToAgent(TopicId topic)
    {
        if (!this.Matches(topic))
        {
            throw new InvalidOperationException("TopicId does not match the subscription.");
        }

        return new AgentId(this.AgentType, topic.Source); // No need for .Name, since AgentType implicitly converts to string
    }

    /// <summary>
    /// Determines whether the specified object is equal to the current subscription.
    /// </summary>
    /// <param name="obj">The object to compare with the current instance.</param>
    /// <returns><c>true</c> if the specified object is equal to this instance; otherwise, <c>false</c>.</returns>
    public override bool Equals([NotNullWhen(true)] object? obj)
    {
        return
            obj is TypePrefixSubscription other &&
                (this.Id == other.Id ||
                    (this.AgentType == other.AgentType &&
                     this.TopicTypePrefix == other.TopicTypePrefix));
    }

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Always call `if (subscription.Matches(topic))` before MapToAgent.
  2. Verify the TopicTypePrefix matches (is a prefix of) the topic types you publish.
  3. Let the runtime perform the mapping rather than calling MapToAgent manually.

Example fix

// before
var agentId = subscription.MapToAgent(topic);

// after
if (!subscription.Matches(topic)) { /* skip / log */ return; }
var agentId = subscription.MapToAgent(topic);
Defensive patterns

Strategy: validation

Validate before calling

if (!subscription.Matches(topic))
{
    // topic does not match this prefix subscription; skip
    return;
}
AgentId target = subscription.MapToAgent(topic);

Type guard

bool Handles(TypePrefixSubscription sub, TopicId t) => t.Type.StartsWith(sub.TopicTypePrefix, StringComparison.Ordinal);

Prevention

When it happens

Trigger: Calling subscription.MapToAgent(topic) directly without first confirming Matches(topic); a topic whose Type does not start with the configured TopicTypePrefix; concurrent mutation of the topic between the Matches and MapToAgent calls.

Common situations: Custom subscription orchestration code that skips the Matches check; a misconfigured prefix that does not correspond to the topics being published; race where topic is rebuilt between calls.

Related errors


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