microsoft/semantic-kernel · error · InvalidOperationException
Message must have a receiver to be sent.
Error message
Message must have a receiver to be sent.
What it means
SendMessageServicerAsync (the internal servicer wired by ForSend) throws InvalidOperationException if envelope.Receiver has no value. The public SendMessageAsync always sets the recipient via ForSend, so this throw indicates a MessageEnvelope was routed through the send servicer without a receiver — i.e. internal/custom misuse rather than normal API usage.
Source
Thrown at dotnet/src/Agents/Runtime/InProcess/InProcessRuntime.cs:401
};
AgentId agentId = subscription.MapToAgent(topic);
if (!this.DeliverToSelf && sender.HasValue && sender == agentId)
{
return;
}
IHostableAgent agent = await this.EnsureAgentAsync(agentId).ConfigureAwait(false);
await agent.OnMessageAsync(envelope.Message, messageContext).ConfigureAwait(false);
}
}
private async ValueTask<object?> SendMessageServicerAsync(MessageEnvelope envelope, CancellationToken deliveryToken)
{
if (!envelope.Receiver.HasValue)
{
throw new InvalidOperationException("Message must have a receiver to be sent.");
}
using CancellationTokenSource combinedSource = CancellationTokenSource.CreateLinkedTokenSource(envelope.Cancellation, deliveryToken);
MessageContext messageContext = new(envelope.MessageId, combinedSource.Token)
{
Sender = envelope.Sender,
IsRpc = false
};
AgentId receiver = envelope.Receiver.Value;
IHostableAgent agent = await this.EnsureAgentAsync(receiver).ConfigureAwait(false);
return await agent.OnMessageAsync(envelope.Message, messageContext).ConfigureAwait(false);
}
private async ValueTask<IHostableAgent> EnsureAgentAsync(AgentId agentId)
{
if (!this.agentInstances.TryGetValue(agentId, out IHostableAgent? agent))View on GitHub (pinned to c028a0c7dc)
Solutions
- Always send via the public SendMessageAsync(message, recipient, ...) which guarantees the receiver is set.
- When building a MessageEnvelope manually for send, always provide a recipient AgentId to ForSend.
- Keep publish and send servicers/servicing paths separate.
Example fix
// before (custom plumbing)
var env = new MessageEnvelope(msg, id, ct);
env.ForSend(recipient: null, sendServicer); // receiver never set
// after
await runtime.SendMessageAsync(msg, new AgentId("worker", "1")); Defensive patterns
Strategy: validation
Validate before calling
// Always send through the public API which sets the receiver.
await runtime.SendMessageAsync(message, new AgentId("worker", "1"));
// If building an envelope manually, ensure a recipient AgentId is supplied to ForSend. Type guard
bool HasReceiver(MessageEnvelope env) => env.Receiver.HasValue;
Prevention
- Use SendMessageAsync rather than wiring servicers manually.
- Never route a publish-oriented envelope through the send servicer.
- When extending the runtime, preserve the receiver through to the servicer.
When it happens
Trigger: Constructing a MessageEnvelope and wiring ForSend without a receiver; a custom servicer reusing SendMessageServicerAsync on a publish envelope; envelope mutation that clears the receiver before servicing.
Common situations: Extending the runtime and mis-wiring servicers; custom delivery plumbing bugs; passing null recipient through internal paths.
Related errors
- Delivery of message {messageId} was cancelled.
- Message must have a topic to be published.
- Runtime is already running.
- Runtime is already stopping.
- Agent with name {agentId.Type} is not of type {typeof(TAgent
AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13).
Data as JSON: /api/errors/6be71e6d109beec5.
Report an issue: GitHub.