Repository navigation
.NET: [Bug]: InvalidDataException when resuming from Checkpoint with new AIAgent.Id values #4793
Description
Activity
- addedbugUsage: [Issues], Target: all issues (Legacy, prefer issue type: bug)Usage: [Issues], Target: all issues (Legacy, prefer issue type: bug).NETUsage: [Issues, PRs], Target: .NetUsage: [Issues, PRs], Target: .Net
on Mar 19, 2026 - addedtriageUsage: [Issues], Target: All issues that still need to be triagedUsage: [Issues], Target: All issues that still need to be triaged
on Mar 19, 2026 - added and removedtriageUsage: [Issues], Target: All issues that still need to be triagedUsage: [Issues], Target: All issues that still need to be triaged
on Mar 19, 2026 The compatibility check is generally about workflow structure and types on the executors. If the structure changed, the checkpoint is considered incompatible.
marcominerva commented
on Mar 25, 2026 ContributorAuthorMore actionsAnd so, Jacob Alber (@lokitoth), in my scenario how can I solve the issue?
Marco Minerva (@marcominerva) Still need to investigate precisely why the verifier thinks the structure changed. I was just adding some context for Peter Ibekwe (@peibekwe)
Reacted by Marco Minerva- addedworkflowsUsage: [Issues, PRs], Target: WorkflowsUsage: [Issues, PRs], Target: Workflowsneeds_pmUsage: [Issues, PRs], Target: features needing PM input or new dependency approvalUsage: [Issues, PRs], Target: features needing PM input or new dependency approvaland removed
on Mar 26, 2026 1 remaining item
Hey Marco Minerva (@marcominerva) - you wouldn't happen to have a copy of the serialized checkpoint that this is happening to, would you?
marcominerva commented
on May 26, 2026 ContributorAuthorMore actionsHi Jacob Alber (@lokitoth), I don't have it right now, but I can reliably reproduce the issue even with the latest versions of the Agent Framework packages. How can I obtain this checkpoint?
If you set things up to use the
FileSystemJsonCheckpointManager:// Set up the CheckpointManager var maybeJsonSerializationOptions = haveCustomJsonTypes ? <something> : null; var checkpointStore = new FileSystemJsonCheckpointStore(<directory>) var checkpointManager = CheckpointManager.CreateJson(checkpointStore, maybeJsonSerializationOptions) // In your .AsAgentCall .AsAIAgent(id: key, name: key, InProcessExecution.Concurrent.WithCheckpointing(checkpointManager));
When the checkpointing happens, it will write the checkpoint to that folder, along with an index. If you do not specify this, it will use InMemory checkpointing and rely on that to generate the JSON-serializable state that gets put in the session state store.
The other thing to consider is that now that AgentSession has the ability to store more arbitrary data in a generalized form, it might be a good idea to move the state data from being a custom AgentSession to living in the StateBag -- though this would be a breaking change.
- removedneeds_pmUsage: [Issues, PRs], Target: features needing PM input or new dependency approvalUsage: [Issues, PRs], Target: features needing PM input or new dependency approval
on May 26, 2026 marcominerva commented
on May 26, 2026 ContributorAuthorMore actionsHere you can find the checkpoint: Checkpoint.zip
Found the issue, I think. When you are resuming the workflow after having reinstantiated the AIAgent instances, they have different Guids for IDs and thus the workflow thinks you are not resuming into the same one.
Here's what's going on:
private sealed class IdFixingAgent(string id, AIAgent innerAgent) : DelegatingAIAgent(innerAgent) { protected override string? IdCore => id; /* If you actually use this, you will probably need to overload the Run/RunAsync methods to overwrite the AgentId on the outgoing AgentResponse/AgentResponseUpdate */ } [Theory] [InlineData(true)] [InlineData(false)] internal async Task Checkpoint_Resume_WithReinstantiatedAgents_DoesNotFail_Async(bool useFixedId) { InProcessExecutionEnvironment ee = ExecutionEnvironment.InProcess_OffThread.ToWorkflowExecutionEnvironment(); Workflow workflow = CreateWorkflow(); CheckpointManager checkpointManager = CheckpointManager.CreateJson(new InMemoryJsonStore()); ee = ee.WithCheckpointing(checkpointManager); Run firstRun = await ee.RunAsync(workflow, "Hello", sessionId: "SessionId"); SuperStepCompletedEvent lastStep = firstRun.OutgoingEvents.OfType<SuperStepCompletedEvent>().Last(); // Verify and extract the last checkpoint. CheckpointInfo checkpoint = lastStep.CompletionInfo.Should().NotBeNull() .And.Subject.As<SuperStepCompletionInfo>() .Checkpoint.Should().NotBeNull() .And.Subject.As<CheckpointInfo>(); workflow = CreateWorkflow(); // Reinstantiate the workflow to ensure agents are reinstantiated as well. StreamingRun resumed = await ee.ResumeStreamingAsync(workflow, checkpoint); // throws when `useFixedId != true` due to agent ID mismatch await resumed.TrySendMessageAsync("Hello again"); await resumed.TrySendMessageAsync(new TurnToken()); await resumed.RunToCompletionAsync(evt => default); Workflow CreateWorkflow() { MockChatClient chatClient = new( (messages, options) => new ChatResponse([new ChatMessage(ChatRole.Assistant, "Hello")])); AIAgent innerAgent = chatClient.AsAIAgent(); AIAgent agent = useFixedId ? new IdFixingAgent("FixedId", innerAgent) : innerAgent; WorkflowBuilder builder = new(agent); return builder.WithOutputFrom(agent) .Build(); } }
Marco Minerva (@marcominerva): In the short term you can use something like
IdFixingAgentto work around this issue. I need to figure out what the right solution here is.Reacted by Marco Minerva- changed the title
[-].NET: [Bug]: InvalidDataException when using Workflow as Agent with Handoff orchestration[/-][+].NET: [Bug]: InvalidDataException when resuming from Checkpoint with new AIAgent.Id values[/+]on May 28, 2026 Hi Marco Minerva (@marcominerva), thanks for the detailed repro; it made the root cause easier to pin down.
Here's what's happening:
Your inner agents (triage_agent,math_tutor,history_tutor) set only aName, not anId. When an agent has no explicitId, it gets a new random id every time it's constructed. Because you register the workflow asScoped, each request rebuilds these agents with fresh ids, which changes the executor identities inside the workflow. The checkpoint stored on the first request records the original executor identities, so on the second request the reconstructed workflow no longer matches it; and resume fails. This is the checkpoint doing an intentional safety check, not data corruption.The Fix:
Give every inner agent a stable, deterministicId(a fixed logical role - not the conversationId /user id). Your agentNames are already constant, which is important too: when aNameis set, the executor identity includes it, so it must stay stable across reconstructions.var triageAgent = chatClient.AsAIAgent(new() { Id = "triage-agent", // stable across scopes/requests Name = "triage_agent", Description = "...", ChatOptions = new() { Instructions = "..." }, ChatHistoryProvider = chatHistoryProvider }, loggerFactory: loggerFactory, services: services); // Do the same for the others: // Id = "math-tutor" (Name = "math_tutor") // Id = "history-tutor" (Name = "history_tutor")With stable ids, the workflow rebuilds to the same executor identities each request and the checkpoint resume correctly; scoped registration is totally fine.
We've also added regression tests and sample guidance for exactly this scenario in #7415 (and are updating the docs to make the stable-id requirement clearer), so this is easier to discover going forward.
Happy to take a closer look if you still see issues after pinning the ids.
marcominerva commented
on Aug 7, 2026 ContributorAuthorMore actionsThank you Peter Ibekwe (@peibekwe). Using a fixed ID solved the issue.
Reacted by Peter Ibekwe
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Description
I'm working on a Web API application and I want to create a Workflow using an Agent with Handoff, as I need to handle a fairly complex scenario. Specifically, I need to call tools and AI context providers that depend on the request (for example, because they need access to a
DbContext). For this reason, I'm trying to register my agents as scoped.To implement this scenario, I encountered a problem that I was able to reproduce with this simplified version of the code:
Note that in this example I haven't defined any tools or context providers that require a scoped context, but they are present in the real application. I'm also using a custom
AgentSessionStorethat relies on aconversationIdprovided by the user (this works for my use case, and I'm confident it is unique for each user). Of course, in the real application the data storage logic is more complex.The first time I invoke the
/api/chatendpoint, it works as expected, but on the second invocation I get this exception:I'm wondering if this is somehow related to the issue #3620.
Package Versions
Microsoft.Agents.AI.Hosting: 1.0.0-preview.260311.1, Microsoft.Agents.AI.OpenAI: 1.0.0-rc4
.NET Version
.NET 10