{"record":{"id":"fcf57e6cf7688d3f","repo":"microsoft/autogen","slug":"message-must-have-a-topic-to-be-published","errorCode":null,"errorMessage":"Message must have a topic to be published.","messagePattern":"Message must have a topic to be published\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"dotnet/src/Microsoft.AutoGen/Core/InProcessRuntime.cs","lineNumber":42,"sourceCode":"    }\n\n    private ValueTask ExecuteTracedAsync(Func<ValueTask> func)\n    {\n        // TODO: Bind tracing\n        return func();\n    }\n\n    public InProcessRuntime()\n    {\n    }\n\n    private ConcurrentQueue<MessageDelivery> messageDeliveryQueue = new();\n\n    private async ValueTask PublishMessageServicer(MessageEnvelope envelope, CancellationToken deliveryToken)\n    {\n        if (!envelope.Topic.HasValue)\n        {\n            throw new InvalidOperationException(\"Message must have a topic to be published.\");\n        }\n\n        TopicId topic = envelope.Topic.Value;\n        List<Exception> exceptions = new();\n\n        foreach (var subscription in this.subscriptions.Values.Where(subscription => subscription.Matches(topic)))\n        {\n            try\n            {\n                deliveryToken.ThrowIfCancellationRequested();\n\n                AgentId? sender = envelope.Sender;\n\n                CancellationTokenSource combinedSource = CancellationTokenSource.CreateLinkedTokenSource(envelope.Cancellation, deliveryToken);\n                MessageContext messageContext = new(envelope.MessageId, combinedSource.Token)\n                {\n                    Sender = sender,\n                    Topic = topic,","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/dotnet/src/Microsoft.AutoGen/Core/InProcessRuntime.cs#L24-L60","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nvar envelope = new MessageEnvelope(message);\nawait runtime.PublishMessageServicer(envelope, ct); // Topic unset -> throws\n\n// after\nvar envelope = new MessageEnvelope(message) { Topic = new TopicId(\"mytopic\", \"mysource\") };\nawait runtime.PublishMessageServicer(envelope, ct);","handlingStrategy":"validation","validationCode":"if (envelope.Topic is null) throw new InvalidOperationException($\"Envelope for {envelope.Message?.GetType().Name} needs a TopicId before publishing\");","typeGuard":"static bool IsPublishable(MessageEnvelope e) => e.Topic.HasValue;","tryCatchPattern":"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); }","preventionTips":["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"],"tags":["in-process","publish","topic","validation"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}