microsoft/autogen · error · InvalidOperationException
Message must have a topic to be published.
Error message
Message must have a topic to be published.
What it means
Thrown by InProcessRuntime's publish servicer when a MessageEnvelope has no Topic set (envelope.Topic is a null Nullable<TopicId>). The in-process runtime routes purely by topic subscription matching, so a topic-less envelope cannot be delivered and is rejected.
Source
Thrown at dotnet/src/Microsoft.AutoGen/Core/InProcessRuntime.cs:42
}
private ValueTask ExecuteTracedAsync(Func<ValueTask> func)
{
// TODO: Bind tracing
return func();
}
public InProcessRuntime()
{
}
private ConcurrentQueue<MessageDelivery> messageDeliveryQueue = new();
private async ValueTask PublishMessageServicer(MessageEnvelope envelope, CancellationToken deliveryToken)
{
if (!envelope.Topic.HasValue)
{
throw new InvalidOperationException("Message must have a topic to be published.");
}
TopicId topic = envelope.Topic.Value;
List<Exception> exceptions = new();
foreach (var subscription in this.subscriptions.Values.Where(subscription => subscription.Matches(topic)))
{
try
{
deliveryToken.ThrowIfCancellationRequested();
AgentId? sender = envelope.Sender;
CancellationTokenSource combinedSource = CancellationTokenSource.CreateLinkedTokenSource(envelope.Cancellation, deliveryToken);
MessageContext messageContext = new(envelope.MessageId, combinedSource.Token)
{
Sender = sender,
Topic = topic,View on GitHub (pinned to 027ecf0a37)
Solutions
- Set envelope.Topic = new TopicId(type, source) before publishing
- Use the runtime's typed publish API (PublishMessageAsync(message, topic)) so the topic is a required parameter
- For direct agent-to-agent sends, use the send/request path rather than topic publish
Example fix
// before
var envelope = new MessageEnvelope(message);
await runtime.PublishMessageServicer(envelope, ct); // Topic unset -> throws
// after
var envelope = new MessageEnvelope(message) { Topic = new TopicId("mytopic", "mysource") };
await runtime.PublishMessageServicer(envelope, ct); Defensive patterns
Strategy: validation
Validate before calling
if (envelope.Topic is null) throw new InvalidOperationException($"Envelope for {envelope.Message?.GetType().Name} needs a TopicId before publishing"); Type guard
static bool IsPublishable(MessageEnvelope e) => e.Topic.HasValue;
Try / catch
try { await runtime.PublishMessageServicer(envelope, ct); } catch (InvalidOperationException ex) when (ex.Message.Contains("must have a topic")) { _logger.LogError(ex, "Rejected topic-less envelope for {Type}", envelope.Message?.GetType().Name); } Prevention
- Use typed publish APIs where TopicId is a required parameter
- Set Topic in the envelope initializer whenever building envelopes manually
- Use send/request paths for direct delivery, not topic publish
When it happens
Trigger: Calling InProcessRuntime publish APIs with a MessageEnvelope whose Topic was never assigned; constructing envelopes manually (new MessageEnvelope(message)) and passing to publish without setting Topic; wrapping/publishing code that drops the Topic field.
Common situations: Custom envelope-building helpers that forget Topic; sending messages intended for direct agent dispatch through the publish path instead of the send path; refactor from topic-required to optional-topic APIs.
Related errors
- Failed to list MCP prompts
- Failed to get MCP prompt
- Failed to get MCP capabilities
- Failed to call MCP tool
- Invalid server URL configuration
AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15).
Data as JSON: /api/errors/fcf57e6cf7688d3f.
Report an issue: GitHub.