diff --git a/src/Temporalio/Client/TemporalClient.Workflow.cs b/src/Temporalio/Client/TemporalClient.Workflow.cs index 2cd6daac..adbabb6e 100644 --- a/src/Temporalio/Client/TemporalClient.Workflow.cs +++ b/src/Temporalio/Client/TemporalClient.Workflow.cs @@ -253,7 +253,8 @@ public override async Task> StartUpdateWithS } catch (Exception e) { - // If this is a multi-operation failure, set exception to the first non-aborted + // If this is a multi-operation failure, set exception to the first present, + // non-OK, non-aborted error if (e is RpcException rpcErr) { var status = rpcErr.GrpcStatus.Value; @@ -261,11 +262,20 @@ public override async Task> StartUpdateWithS { if (status.Details[0].TryUnpack(out Api.ErrorDetails.V1.MultiOperationExecutionFailure failure)) { - var nonAborted = failure.Statuses.FirstOrDefault(s => s.Details.Count == 0 || - !s.Details[0].Is(Api.Failure.V1.MultiOperationExecutionAborted.Descriptor)); - var grpcStatus = new GrpcStatus() { Code = nonAborted.Code, Message = nonAborted.Message }; - grpcStatus.Details.AddRange(nonAborted.Details); - e = new RpcException(grpcStatus); + var nonAborted = failure.Statuses.FirstOrDefault(s => + // Exists + s != null && + // Not ok + s.Code != (int)RpcException.StatusCode.OK && + // Not aborted + (s.Details.Count == 0 || + !s.Details[0].Is(Api.Failure.V1.MultiOperationExecutionAborted.Descriptor))); + if (nonAborted != null) + { + var grpcStatus = new GrpcStatus() { Code = nonAborted.Code, Message = nonAborted.Message }; + grpcStatus.Details.AddRange(nonAborted.Details); + e = new RpcException(grpcStatus); + } } } } @@ -462,7 +472,7 @@ public async override Task> StartWorkflowUpdateAsy // If the requested stage is completed, wait for result, but discard the update // exception, that will come when _they_ call get result var handle = new WorkflowUpdateHandle( - Client, req.Request.Meta.UpdateId, input.Id, input.RunId) + Client, req.Request.Meta.UpdateId, input.Id, resp.UpdateRef.WorkflowExecution.RunId) { KnownOutcome = resp.Outcome }; if (input.Options.WaitForStage == WorkflowUpdateStage.Completed) { diff --git a/tests/Temporalio.Tests/Worker/WorkflowWorkerTests.cs b/tests/Temporalio.Tests/Worker/WorkflowWorkerTests.cs index a087e48d..d5794e45 100644 --- a/tests/Temporalio.Tests/Worker/WorkflowWorkerTests.cs +++ b/tests/Temporalio.Tests/Worker/WorkflowWorkerTests.cs @@ -3697,10 +3697,13 @@ await ExecuteWorkerAsync(async worker => (UpdateWorkflow wf) => wf.RunAsync(), new(id: $"workflow-{Guid.NewGuid()}", taskQueue: worker.Options.TaskQueue!)); - // Make all possible overload calls via start then get response - await (await ((WorkflowHandle)handle).StartUpdateAsync( + // Make all possible overload calls via start then get response. For + // the first update we'll also confirm the run ID is set. + var updateHandle = await ((WorkflowHandle)handle).StartUpdateAsync( (UpdateWorkflow wf) => wf.DoUpdateNoParamNoResponseAsync(), - new(WorkflowUpdateStage.Accepted))).GetResultAsync(); + new(WorkflowUpdateStage.Accepted)); + Assert.NotNull(updateHandle.WorkflowRunId); + await updateHandle.GetResultAsync(); Assert.Equal( $"no-param-response: {handle.Id}", await (await ((WorkflowHandle)handle).StartUpdateAsync(