microsoft/semantic-kernel · error · KernelException
Step {this.Name} received message from Step named '{message.
Error message
Step {this.Name} received message from Step named '{message.SourceId}' with group Id '{message.GroupId}' that is not registered. What it means
Thrown by LocalStep.HandleMessageAsync when a message has a non-empty GroupId that is not found in the step's _edgeGroupProcessors dictionary. Edge group processors are built from _stepInfo.IncomingEdgeGroups during construction; a GroupId on an incoming message that doesn't match any registered group means the source step's edge declared a group that the target step never registered.
Source
Thrown at dotnet/src/Experimental/Process.LocalRuntime/LocalStep.cs:196
Verify.NotNull(message, nameof(message));
// Lazy one-time initialization of the step before processing a message
await this._initializeTask.Value.ConfigureAwait(false);
if (this._functions is null || this._inputs is null || this._initialInputs is null)
{
throw new KernelException("The step has not been initialized.").Log(this._logger);
}
string messageLogParameters = string.Join(", ", message.Values.Select(kvp => $"{kvp.Key}: {kvp.Value}"));
this._logger.LogDebug("Received message from '{SourceId}' targeting function '{FunctionName}' and parameters '{Parameters}'.", message.SourceId, message.FunctionName, messageLogParameters);
if (!string.IsNullOrEmpty(message.GroupId))
{
this._logger.LogDebug("Step {StepName} received message from Step named '{SourceId}' with group Id '{GroupId}'.", this.Name, message.SourceId, message.GroupId);
if (!this._edgeGroupProcessors.TryGetValue(message.GroupId, out LocalEdgeGroupProcessor? edgeGroupProcessor) || edgeGroupProcessor is null)
{
throw new KernelException($"Step {this.Name} received message from Step named '{message.SourceId}' with group Id '{message.GroupId}' that is not registered.").Log(this._logger);
}
if (!edgeGroupProcessor.TryGetResult(message, out Dictionary<string, object?>? result))
{
// The edge group processor has not received all required messages yet.
return;
}
// The edge group processor has received all required messages and has produced a result.
message = message with { Values = result ?? [] };
}
// Add the message values to the inputs for the function
this.AssignStepFunctionParameterValues(message);
// If we're still waiting for inputs on all of our functions then don't do anything.
List<string> invocableFunctions = this._inputs.Where(i => i.Value != null && i.Value.All(v => v.Value != null)).Select(i => i.Key).ToList();
var missingKeys = this._inputs.Where(i => i.Value is null || i.Value.Any(v => v.Value is null));View on GitHub (pinned to c028a0c7dc)
Solutions
- Ensure every GroupId used on source-side edges has a matching KernelProcessEdgeGroup entry in the target step's IncomingEdgeGroups.
- Verify the GroupId string matches exactly (case-sensitive) between the edge and the group definition.
- Check that the edge's target step id is correct so messages reach the step that owns the group.
Example fix
// before - edge declares group 'fanin1' but target step has no such group
source.OnEvent("evt").To(target, "func", groupId: "fanin1");
// after - register the group on the target step
target.IncomingEdgeGroups["fanin1"] = new KernelProcessEdgeGroup("fanin1") { MessageSources = [...] }; Defensive patterns
Strategy: validation
Validate before calling
// Validate that every GroupId on incoming edges is registered on the target step
var registeredGroups = step.IncomingEdgeGroups?.Keys.ToHashSet() ?? new HashSet<string>();
foreach (var edgeList in process.Edges.Values)
{
foreach (var edge in edgeList)
{
if (edge.OutputTarget is KernelProcessFunctionTarget ft && ft.StepId == step.State.Id && !string.IsNullOrEmpty(ft.GroupId))
{
if (!registeredGroups.Contains(ft.GroupId)) { /* configuration error */ }
}
}
} Prevention
- Register every KernelProcessEdgeGroup on the target step before defining edges that reference its GroupId.
- Use constants for GroupId strings to avoid mismatches.
- Run a pre-start validation pass that cross-references edge GroupIds with step IncomingEdgeGroups.
When it happens
Trigger: A source step's outgoing edge specifies a GroupId, and the message carries it to a target step, but the target step's IncomingEdgeGroups does not contain an entry for that GroupId. The lookup at line 194 fails.
Common situations: Adding a GroupId to an edge on the source side without declaring the corresponding KernelProcessEdgeGroup on the target step; renaming a GroupId on one side but not the other; using the wrong target step id so the message reaches a step that doesn't know about the group.
Related errors
- Message {messageKey} is not expected for edge group {this._e
- The proxy step can only handle 1 parameter object
- External message channel not configured for step with topic
- External message channel not configured for step
- The process must have an Id set
AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13).
Data as JSON: /api/errors/0a641d9ed8a3a20c.
Report an issue: GitHub.