From b6168ef7b88cb9f8c4f200aed3f6e9c07813b855 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 17 Jan 2026 01:00:55 +0000 Subject: [PATCH 1/4] Initial plan From 59dfa2ed1b4d89b77fb6d23ea640045171aeae1f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 17 Jan 2026 01:06:54 +0000 Subject: [PATCH 2/4] Fix NullReferenceException by adding EntityConversionState to history conversions Co-authored-by: YunchuWang <12449837+YunchuWang@users.noreply.github.com> --- src/Client/Grpc/GrpcDurableTaskClient.cs | 11 ++++++++++- src/Worker/Grpc/GrpcOrchestrationRunner.cs | 10 +++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Client/Grpc/GrpcDurableTaskClient.cs b/src/Client/Grpc/GrpcDurableTaskClient.cs index 20cc348b..9b3d378f 100644 --- a/src/Client/Grpc/GrpcDurableTaskClient.cs +++ b/src/Client/Grpc/GrpcDurableTaskClient.cs @@ -523,10 +523,19 @@ public override async Task> GetOrchestrationHistoryAsync( using AsyncServerStreamingCall streamResponse = this.sidecarClient.StreamInstanceHistory(streamRequest, cancellationToken: cancellation); + Microsoft.DurableTask.ProtoUtils.EntityConversionState? entityConversionState = + this.options.EnableEntitySupport + ? new(insertMissingEntityUnlocks: false) + : null; + + Func converter = entityConversionState is null + ? Microsoft.DurableTask.ProtoUtils.ConvertHistoryEvent + : entityConversionState.ConvertFromProto; + List pastEvents = []; while (await streamResponse.ResponseStream.MoveNext(cancellation)) { - pastEvents.AddRange(streamResponse.ResponseStream.Current.Events.Select(DurableTask.ProtoUtils.ConvertHistoryEvent)); + pastEvents.AddRange(streamResponse.ResponseStream.Current.Events.Select(converter)); } return pastEvents; diff --git a/src/Worker/Grpc/GrpcOrchestrationRunner.cs b/src/Worker/Grpc/GrpcOrchestrationRunner.cs index 5fbe2228..60afa547 100644 --- a/src/Worker/Grpc/GrpcOrchestrationRunner.cs +++ b/src/Worker/Grpc/GrpcOrchestrationRunner.cs @@ -124,11 +124,15 @@ public static string LoadAndRun( P.OrchestratorRequest request = P.OrchestratorRequest.Parser.Base64Decode( encodedOrchestratorRequest); - List pastEvents = request.PastEvents.Select(ProtoUtils.ConvertHistoryEvent).ToList(); - IEnumerable newEvents = request.NewEvents.Select(ProtoUtils.ConvertHistoryEvent); + Microsoft.DurableTask.ProtoUtils.EntityConversionState? entityConversionState = new(insertMissingEntityUnlocks: false); + + Func converter = entityConversionState.ConvertFromProto; + + List pastEvents = request.PastEvents.Select(converter).ToList(); + IEnumerable newEvents = request.NewEvents.Select(converter); Dictionary properties = request.Properties.ToDictionary( pair => pair.Key, - pair => ProtoUtils.ConvertValueToObject(pair.Value)); + pair => Microsoft.DurableTask.ProtoUtils.ConvertValueToObject(pair.Value)); OrchestratorExecutionResult? result = null; From 1ae614602d6cefbba6287db18bf9239dad1e3f7b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 17 Jan 2026 01:12:00 +0000 Subject: [PATCH 3/4] Address code review feedback: remove nullable modifier and use consistent naming Co-authored-by: YunchuWang <12449837+YunchuWang@users.noreply.github.com> --- src/Worker/Grpc/GrpcOrchestrationRunner.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Worker/Grpc/GrpcOrchestrationRunner.cs b/src/Worker/Grpc/GrpcOrchestrationRunner.cs index 60afa547..c3e91bd1 100644 --- a/src/Worker/Grpc/GrpcOrchestrationRunner.cs +++ b/src/Worker/Grpc/GrpcOrchestrationRunner.cs @@ -124,7 +124,7 @@ public static string LoadAndRun( P.OrchestratorRequest request = P.OrchestratorRequest.Parser.Base64Decode( encodedOrchestratorRequest); - Microsoft.DurableTask.ProtoUtils.EntityConversionState? entityConversionState = new(insertMissingEntityUnlocks: false); + ProtoUtils.EntityConversionState entityConversionState = new(insertMissingEntityUnlocks: false); Func converter = entityConversionState.ConvertFromProto; @@ -132,7 +132,7 @@ public static string LoadAndRun( IEnumerable newEvents = request.NewEvents.Select(converter); Dictionary properties = request.Properties.ToDictionary( pair => pair.Key, - pair => Microsoft.DurableTask.ProtoUtils.ConvertValueToObject(pair.Value)); + pair => ProtoUtils.ConvertValueToObject(pair.Value)); OrchestratorExecutionResult? result = null; From 99ee0e732ad0d5e73d48c799e6e9edf7dba59659 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 17 Jan 2026 01:49:35 +0000 Subject: [PATCH 4/4] Remove EnableEntitySupport condition - always create EntityConversionState The EntityConversionState should always be created regardless of the EnableEntitySupport option because: 1. History may contain entity events even if the client option is disabled 2. The orchestration instance ID is automatically set when ExecutionStarted event is processed 3. This matches the pattern used in GrpcDurableTaskWorker.Processor Co-authored-by: YunchuWang <12449837+YunchuWang@users.noreply.github.com> --- src/Client/Grpc/GrpcDurableTaskClient.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Client/Grpc/GrpcDurableTaskClient.cs b/src/Client/Grpc/GrpcDurableTaskClient.cs index 9b3d378f..d2007372 100644 --- a/src/Client/Grpc/GrpcDurableTaskClient.cs +++ b/src/Client/Grpc/GrpcDurableTaskClient.cs @@ -523,19 +523,12 @@ public override async Task> GetOrchestrationHistoryAsync( using AsyncServerStreamingCall streamResponse = this.sidecarClient.StreamInstanceHistory(streamRequest, cancellationToken: cancellation); - Microsoft.DurableTask.ProtoUtils.EntityConversionState? entityConversionState = - this.options.EnableEntitySupport - ? new(insertMissingEntityUnlocks: false) - : null; - - Func converter = entityConversionState is null - ? Microsoft.DurableTask.ProtoUtils.ConvertHistoryEvent - : entityConversionState.ConvertFromProto; + Microsoft.DurableTask.ProtoUtils.EntityConversionState entityConversionState = new(insertMissingEntityUnlocks: false); List pastEvents = []; while (await streamResponse.ResponseStream.MoveNext(cancellation)) { - pastEvents.AddRange(streamResponse.ResponseStream.Current.Events.Select(converter)); + pastEvents.AddRange(streamResponse.ResponseStream.Current.Events.Select(entityConversionState.ConvertFromProto)); } return pastEvents;