Skip to content

Commit d219870

Browse files
authored
Check for grpc status details before indexing & unpacking (#801)
* Check for grpc status details before indexing & unpacking * fix test - mock workflow service call instead of using interceptor (interceptor does not call code)
1 parent b0dfaef commit d219870

File tree

2 files changed

+53
-3
lines changed

2 files changed

+53
-3
lines changed

temporalio/client.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -6073,7 +6073,9 @@ def on_start(
60736073
multiop_failure = (
60746074
temporalio.api.errordetails.v1.MultiOperationExecutionFailure()
60756075
)
6076-
if err.grpc_status.details[0].Unpack(multiop_failure):
6076+
if err.grpc_status.details and err.grpc_status.details[0].Unpack(
6077+
multiop_failure
6078+
):
60776079
status = next(
60786080
(
60796081
st
@@ -6108,7 +6110,6 @@ def on_start(
61086110
RPCStatusCode(status.code),
61096111
err.raw_grpc_status,
61106112
)
6111-
61126113
raise err
61136114
finally:
61146115
if err and not seen_start:

tests/worker/test_update_with_start.py

+50-1
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
from dataclasses import dataclass
66
from datetime import timedelta
77
from enum import Enum
8-
from typing import Any, Iterator
8+
from typing import Any, Iterator, Mapping, Optional
99
from unittest.mock import patch
1010

1111
import pytest
1212

13+
import temporalio.api.common.v1
14+
import temporalio.api.workflowservice.v1
1315
from temporalio import activity, workflow
1416
from temporalio.client import (
1517
Client,
@@ -25,6 +27,7 @@
2527
WorkflowIDConflictPolicy,
2628
)
2729
from temporalio.exceptions import ApplicationError, WorkflowAlreadyStartedError
30+
from temporalio.service import RPCError, RPCStatusCode, ServiceCall
2831
from temporalio.testing import WorkflowEnvironment
2932
from tests.helpers import (
3033
new_worker,
@@ -805,3 +808,49 @@ async def test_update_with_start_two_param(client: Client):
805808
assert await wf_handle.result() == WorkflowResult(
806809
result="workflow-arg1-workflow-arg2"
807810
)
811+
812+
813+
# Verify correcting issue #791
814+
async def test_start_update_with_start_empty_details(client: Client):
815+
class execute_multi_operation(
816+
ServiceCall[
817+
temporalio.api.workflowservice.v1.ExecuteMultiOperationRequest,
818+
temporalio.api.workflowservice.v1.ExecuteMultiOperationResponse,
819+
]
820+
):
821+
empty_details_err = RPCError("empty details", RPCStatusCode.INTERNAL, b"")
822+
# Set grpc_status with empty details
823+
empty_details_err._grpc_status = temporalio.api.common.v1.GrpcStatus(details=[])
824+
825+
def __init__(self) -> None:
826+
pass
827+
828+
async def __call__(
829+
self,
830+
req: temporalio.api.workflowservice.v1.ExecuteMultiOperationRequest,
831+
*,
832+
retry: bool = False,
833+
metadata: Mapping[str, str] = {},
834+
timeout: Optional[timedelta] = None,
835+
) -> temporalio.api.workflowservice.v1.ExecuteMultiOperationResponse:
836+
raise self.empty_details_err
837+
838+
with patch.object(
839+
client.workflow_service, "execute_multi_operation", execute_multi_operation()
840+
):
841+
with pytest.raises(RPCError) as err:
842+
await client.start_update_with_start_workflow(
843+
UpdateWithStartInterceptorWorkflow.my_update,
844+
"original-update-arg",
845+
start_workflow_operation=WithStartWorkflowOperation(
846+
UpdateWithStartInterceptorWorkflow.run,
847+
"wf-arg",
848+
id=f"wf-{uuid.uuid4()}",
849+
task_queue="tq",
850+
id_conflict_policy=WorkflowIDConflictPolicy.FAIL,
851+
),
852+
wait_for_stage=WorkflowUpdateStage.ACCEPTED,
853+
)
854+
assert err.value.status == RPCStatusCode.INTERNAL
855+
assert err.value.message == "empty details"
856+
assert len(err.value.grpc_status.details) == 0

0 commit comments

Comments
 (0)