{"record":{"id":"8e7c379dd9b11bd0","repo":"microsoft/autogen","slug":"control-message-is-missing-a-destination-message","errorCode":null,"errorMessage":"Control message is missing a destination. Message: '{controlMsg}'","messagePattern":"Control message is missing a destination\\. Message: '(.+?)'","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"dotnet/src/Microsoft.AutoGen/RuntimeGateway.Grpc/Services/Grpc/GrpcGateway.cs","lineNumber":546,"sourceCode":"\n    /// <summary>\n    /// Writes a response to a worker connection.\n    /// </summary>\n    /// <param name=\"connection\">The worker connection.</param>\n    /// <param name=\"cloudEvent\">The cloud event.</param>\n    /// <param name=\"cancellationToken\">The cancellation token.</param>\n    /// <returns>A task that represents the asynchronous operation.</returns>\n    private async Task WriteResponseAsync(GrpcWorkerConnection<Message> connection, CloudEvent cloudEvent, CancellationToken cancellationToken = default)\n    {\n        await connection.ResponseStream.WriteAsync(new Message { CloudEvent = cloudEvent }, cancellationToken).ConfigureAwait(false);\n    }\n\n    private async ValueTask DispatchControlMessageAsync<TMessage>(GrpcWorkerConnection<TMessage> connection, ControlMessage controlMsg, CancellationToken cancellationToken)\n    where TMessage : class\n    {\n        if (string.IsNullOrEmpty(controlMsg.Destination))\n        {\n            throw new InvalidOperationException($\"Control message is missing a destination. Message: '{controlMsg}'\");\n        }\n\n        // Ensure the control message is of the correct type\n        if (controlMsg is TMessage typedResponseMessage)\n        {\n            // Send the response back to the client\n            await connection.ResponseStream.WriteAsync(typedResponseMessage, cancellationToken).ConfigureAwait(false);\n        }\n        else\n        {\n            throw new InvalidOperationException($\"Cannot convert control message to type {typeof(TMessage).Name}\");\n        }\n    }\n}\n","sourceCodeStart":528,"sourceCodeEnd":561,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/dotnet/src/Microsoft.AutoGen/RuntimeGateway.Grpc/Services/Grpc/GrpcGateway.cs#L528-L561","documentation":"InvalidOperationException thrown in GrpcGateway.DispatchControlMessageAsync when a ControlMessage has an empty or null Destination. Control messages are forwarded verbatim to a destination worker over its response stream; with no destination the gateway has no way to route the write, so it refuses the message.","triggerScenarios":"A worker or gateway component emitting a ControlMessage without setting Destination (for example a keep-alive or ack built by hand); string.Empty slipping through a serializer default; control-plane code paths that broadcast rather than target a single worker.","commonSituations":"Custom control-protocol extensions that added new message kinds but forgot the destination field; proto3 field-presence pitfalls where an unset string serializes as \"\" and passes IsNullOrEmpty checks only on the gateway side; version mismatch between gateway and worker control-message schemas.","solutions":["Always populate ControlMessage.Destination with the target worker/agent identity before dispatch.","If the message is meant for the originating worker, set Destination to that worker's client id before sending.","Add a unit test over your control-message builders asserting Destination is non-empty."],"exampleFix":"// before\nvar msg = new ControlMessage { Command = \"reload\" };\n\n// after\nvar msg = new ControlMessage { Destination = clientId, Command = \"reload\" };","handlingStrategy":"validation","validationCode":"if (string.IsNullOrEmpty(controlMsg?.Destination))\n{\n    throw new ArgumentException($\"ControlMessage requires a Destination: {controlMsg}\");\n}","typeGuard":"static bool IsRoutable(ControlMessage msg) => !string.IsNullOrEmpty(msg?.Destination);","tryCatchPattern":"try { await DispatchControlMessageAsync(connection, controlMsg, ct); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"missing a destination\"))\n{\n    _logger.LogError(\"Discarding control message without destination: {@Msg}\", controlMsg);\n}","preventionTips":["Always set Destination when constructing control messages; treat it as required.","Add a builder/factory for control messages that refuses to emit without a destination.","Unit test all control-message constructors for non-empty Destination."],"tags":["dotnet","grpc","control-plane","validation"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}