{"record":{"id":"91aabb78e22a0936","repo":"microsoft/semantic-kernel","slug":"attempt-to-build-a-workflow-node-from-step-with-no","errorCode":null,"errorMessage":"Attempt to build a workflow node from step with no Id","messagePattern":"Attempt to build a workflow node from step with no Id","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"dotnet/src/Experimental/Process.Core/Workflow/WorkflowBuilder.cs","lineNumber":416,"sourceCode":"        }\n\n        workflow.Orchestration = orchestration;\n        return Task.FromResult(workflow);\n    }\n\n    private static Node BuildNode(KernelProcessStepInfo step, List<OrchestrationStep> orchestrationSteps)\n    {\n        Verify.NotNullOrWhiteSpace(step?.State?.Id, nameof(step.State.Id));\n\n        if (step is KernelProcessAgentStep agentStep)\n        {\n            return BuildAgentNode(agentStep, orchestrationSteps);\n        }\n\n        var innerStepTypeString = step.InnerStepType.AssemblyQualifiedName;\n        if (string.IsNullOrWhiteSpace(innerStepTypeString))\n        {\n            throw new InvalidOperationException(\"Attempt to build a workflow node from step with no Id\");\n        }\n\n        var node = new Node()\n        {\n            Id = step.State.Id,\n            Type = \"dotnet\",\n            Agent = new AgentDefinition()\n            {\n                Type = innerStepTypeString,\n                Id = step.State.Id\n            }\n        };\n\n        foreach (var edge in step.Edges)\n        {\n            OrchestrationStep orchestrationStep = new()\n            {\n                ListenFor = new ListenCondition()","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/dotnet/src/Experimental/Process.Core/Workflow/WorkflowBuilder.cs#L398-L434","documentation":"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`.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nprocess.AddStepFromType(RuntimeEmitHelper.GenerateProxy());\n// after\nprocess.AddStepFromType<MyNamedStep>();","handlingStrategy":"validation","validationCode":"foreach (var step in process.Steps)\n{\n    if (string.IsNullOrWhiteSpace(step.InnerStepType?.AssemblyQualifiedName))\n        throw new InvalidOperationException($\"Step '{step.State?.Id}' has an InnerStepType without an AssemblyQualifiedName; use a concrete named type.\");\n}","typeGuard":"bool HasAssemblyQualifiedName(KernelProcessStepInfo step) =>\n    !string.IsNullOrWhiteSpace(step?.InnerStepType?.AssemblyQualifiedName);","tryCatchPattern":"try { await WorkflowBuilder.BuildWorkflow(process); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"no Id\"))\n{ _logger.LogError(ex, \"A step's InnerStepType has no AssemblyQualifiedName.\"); throw; }","preventionTips":["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."],"tags":["workflow","process-framework","reflection","serialization","configuration"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}