microsoft/semantic-kernel · error · InvalidOperationException
Attempt to build a workflow node from step with no Id
Error message
Attempt to build a workflow node from step with no Id
What it means
Thrown by `BuildNode` for a non-agent step when `step.InnerStepType.AssemblyQualifiedName` is null or whitespace. Despite the message text mentioning 'no Id', the real precondition is that the step's underlying .NET type must resolve to a full assembly-qualified name so it can be recorded in the workflow's `Agent.Type`. It is an InvalidOperationException raised before constructing the `Node`.
Source
Thrown at dotnet/src/Experimental/Process.Core/Workflow/WorkflowBuilder.cs:416
}
workflow.Orchestration = orchestration;
return Task.FromResult(workflow);
}
private static Node BuildNode(KernelProcessStepInfo step, List<OrchestrationStep> orchestrationSteps)
{
Verify.NotNullOrWhiteSpace(step?.State?.Id, nameof(step.State.Id));
if (step is KernelProcessAgentStep agentStep)
{
return BuildAgentNode(agentStep, orchestrationSteps);
}
var innerStepTypeString = step.InnerStepType.AssemblyQualifiedName;
if (string.IsNullOrWhiteSpace(innerStepTypeString))
{
throw new InvalidOperationException("Attempt to build a workflow node from step with no Id");
}
var node = new Node()
{
Id = step.State.Id,
Type = "dotnet",
Agent = new AgentDefinition()
{
Type = innerStepTypeString,
Id = step.State.Id
}
};
foreach (var edge in step.Edges)
{
OrchestrationStep orchestrationStep = new()
{
ListenFor = new ListenCondition()View on GitHub (pinned to c028a0c7dc)
Solutions
- Ensure each step uses a concrete, named, assembly-resident type with a non-null `AssemblyQualifiedName`.
- Avoid anonymous/dynamic proxy types as step inner types; wrap logic in a real named class.
- Verify the assembly defining the step type is loaded in the current AppDomain/context before building.
Example fix
// before process.AddStepFromType(RuntimeEmitHelper.GenerateProxy()); // after process.AddStepFromType<MyNamedStep>();
Defensive patterns
Strategy: validation
Validate before calling
foreach (var step in process.Steps)
{
if (string.IsNullOrWhiteSpace(step.InnerStepType?.AssemblyQualifiedName))
throw new InvalidOperationException($"Step '{step.State?.Id}' has an InnerStepType without an AssemblyQualifiedName; use a concrete named type.");
} Type guard
bool HasAssemblyQualifiedName(KernelProcessStepInfo step) =>
!string.IsNullOrWhiteSpace(step?.InnerStepType?.AssemblyQualifiedName); Try / catch
try { await WorkflowBuilder.BuildWorkflow(process); }
catch (InvalidOperationException ex) when (ex.Message.Contains("no Id"))
{ _logger.LogError(ex, "A step's InnerStepType has no AssemblyQualifiedName."); throw; } Prevention
- Register only concrete, assembly-resident types as steps.
- Avoid dynamically emitted/anonymous types as step inner types.
- In trimming/AOT scenarios, ensure step types are root-preserved.
When it happens
Trigger: Pass a `KernelProcessStepInfo` whose `InnerStepType` is a dynamic/anonymous/generic-open type with no resolvable `AssemblyQualifiedName`; load a step from a serialized form that lost its type binding; reference a step type from an unloaded assembly.
Common situations: Using dynamically generated step types (e.g. proxy/emitted types) without stable assembly identity; partial trust or reflection-restricted contexts that suppress `AssemblyQualifiedName`; version/assembly-load issues where the type object is incomplete.
Related errors
- The process must have an Id set
- Unsupported target type
- Failed to deserialize schema.
- The edge target is not a function target: {e.OutputTarget}
- Failed to parse schema.
AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13).
Data as JSON: /api/errors/91aabb78e22a0936.
Report an issue: GitHub.