microsoft/autogen · error · InvalidOperationException
Invalid subscription type
Error message
Invalid subscription type
What it means
InvalidOperationException thrown by RegistryGrain.SetSubscriptionAsync when a Subscription message's SubscriptionCase is neither TypeSubscription nor TypePrefixSubscription. The registry stores subscriptions in per-kind maps, so it must know which oneof case it received; an unset or unknown case hits the default arm.
Source
Thrown at dotnet/src/Microsoft.AutoGen/RuntimeGateway.Grpc/Services/Orleans/RegistryGrain.cs:211
if (topics is null)
{
topics = new HashSet<string>();
state.State.AgentsToTopicsMap[subscription.Subscription.TypeSubscription.AgentType] = topics;
}
topics.Add(subscription.Subscription.TypeSubscription.TopicType);
// add the agent type to the set of agent types for the topic
state.State.TopicToAgentTypesMap.TryGetValue(subscription.Subscription.TypeSubscription.TopicType, out var agents);
if (agents is null)
{
agents = new HashSet<string>();
state.State.TopicToAgentTypesMap[subscription.Subscription.TypeSubscription.TopicType] = agents;
}
agents.Add(subscription.Subscription.TypeSubscription.AgentType);
break;
}
default:
throw new InvalidOperationException("Invalid subscription type");
}
// add the subscription by Guid
state.State.GuidSubscriptionsMap.TryGetValue(guid, out var existingSubscriptions);
if (existingSubscriptions is null)
{
existingSubscriptions = new HashSet<Subscription>();
state.State.GuidSubscriptionsMap[guid] = existingSubscriptions;
}
existingSubscriptions.Add(subscription.Subscription);
await state.WriteStateAsync().ConfigureAwait(false);
}
public async ValueTask UnsubscribeAsync(RemoveSubscriptionRequest request)
{
var guid = request.Id;
// does the guid parse?
if (!Guid.TryParse(guid, out var _))
{
throw new InvalidOperationException("Invalid subscription id");View on GitHub (pinned to 027ecf0a37)
Solutions
- Set exactly one oneof branch before subscribing: subscription.TypeSubscription = new TypeSubscription { TopicType = ..., AgentType = ... } or the TypePrefixSubscription equivalent.
- Guard client-side: throw early if request.Subscription.SubscriptionCase == SubscriptionOneofCase.None.
- Keep publishers and the gateway on proto definitions that agree on the subscription kinds.
Example fix
// before
var sub = new Subscription();
await registry.SetSubscriptionAsync(new SubscriptionRequest { Subscription = sub });
// after
var sub = new Subscription
{
TypeSubscription = new TypeSubscription { TopicType = "events", AgentType = "MyAgent" }
};
await registry.SetSubscriptionAsync(new SubscriptionRequest { Subscription = sub }); Defensive patterns
Strategy: type-guard
Type guard
static bool IsValidSubscription(Subscription s) =>
s.SubscriptionCase is Subscription.SubscriptionOneofCase.TypeSubscription
or Subscription.SubscriptionOneofCase.TypePrefixSubscription;
if (!IsValidSubscription(request.Subscription))
throw new ArgumentException("Subscription must set TypeSubscription or TypePrefixSubscription"); Try / catch
try { await registry.SetSubscriptionAsync(request); }
catch (InvalidOperationException ex) when (ex.Message == "Invalid subscription type")
{
_logger.LogError("Subscription payload missing oneof branch; rejecting request {Id}.", request.Id);
} Prevention
- Always assign exactly one oneof branch when building Subscription messages.
- Check SubscriptionCase == None client-side to reject uninitialized subscriptions early.
- Keep client and gateway protos in sync when adding subscription kinds.
When it happens
Trigger: Sending a SubscribeAsync request whose Subscription message has neither oneof branch set (constructing new Subscription {} and calling subscribe); adding a new SubscriptionOneofCase to the proto without updating this switch; forwarding a corrupt/uninitialized protobuf payload.
Common situations: Client code that builds Subscription imperatively and forgets to set TypeSubscription or TypePrefixSubscription; proto3 oneof semantics where assigning nothing leaves the case at None; SDK version skew introducing a third subscription kind the grain does not know.
Related errors
- Response is null.
- Unexpected message '{message}'.
- INVALID_ARGUMENT
- Request message is missing a target. Message: '{request}'.
- Control message is missing a destination. Message: '{control
AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15).
Data as JSON: /api/errors/78783f4cb8483fc5.
Report an issue: GitHub.