Skip to content

Check for grpc status details before indexing & unpacking #801

New issue

Have a question about this project? No Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “No Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? No Sign in to your account

Merged
merged 3 commits into from
Mar 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions temporalio/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6073,7 +6073,9 @@ def on_start(
multiop_failure = (
temporalio.api.errordetails.v1.MultiOperationExecutionFailure()
)
if err.grpc_status.details[0].Unpack(multiop_failure):
if err.grpc_status.details and err.grpc_status.details[0].Unpack(
multiop_failure
):
status = next(
(
st
Expand Down Expand Up @@ -6108,7 +6110,6 @@ def on_start(
RPCStatusCode(status.code),
err.raw_grpc_status,
)

raise err
finally:
if err and not seen_start:
Expand Down
51 changes: 50 additions & 1 deletion tests/worker/test_update_with_start.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
from dataclasses import dataclass
from datetime import timedelta
from enum import Enum
from typing import Any, Iterator
from typing import Any, Iterator, Mapping, Optional
from unittest.mock import patch

import pytest

import temporalio.api.common.v1
import temporalio.api.workflowservice.v1
from temporalio import activity, workflow
from temporalio.client import (
Client,
Expand All @@ -25,6 +27,7 @@
WorkflowIDConflictPolicy,
)
from temporalio.exceptions import ApplicationError, WorkflowAlreadyStartedError
from temporalio.service import RPCError, RPCStatusCode, ServiceCall
from temporalio.testing import WorkflowEnvironment
from tests.helpers import (
new_worker,
Expand Down Expand Up @@ -805,3 +808,49 @@ async def test_update_with_start_two_param(client: Client):
assert await wf_handle.result() == WorkflowResult(
result="workflow-arg1-workflow-arg2"
)


# Verify correcting issue #791
async def test_start_update_with_start_empty_details(client: Client):
class execute_multi_operation(
ServiceCall[
temporalio.api.workflowservice.v1.ExecuteMultiOperationRequest,
temporalio.api.workflowservice.v1.ExecuteMultiOperationResponse,
]
):
empty_details_err = RPCError("empty details", RPCStatusCode.INTERNAL, b"")
# Set grpc_status with empty details
empty_details_err._grpc_status = temporalio.api.common.v1.GrpcStatus(details=[])

def __init__(self) -> None:
pass

async def __call__(
self,
req: temporalio.api.workflowservice.v1.ExecuteMultiOperationRequest,
*,
retry: bool = False,
metadata: Mapping[str, str] = {},
timeout: Optional[timedelta] = None,
) -> temporalio.api.workflowservice.v1.ExecuteMultiOperationResponse:
raise self.empty_details_err

with patch.object(
client.workflow_service, "execute_multi_operation", execute_multi_operation()
):
with pytest.raises(RPCError) as err:
await client.start_update_with_start_workflow(
UpdateWithStartInterceptorWorkflow.my_update,
"original-update-arg",
start_workflow_operation=WithStartWorkflowOperation(
UpdateWithStartInterceptorWorkflow.run,
"wf-arg",
id=f"wf-{uuid.uuid4()}",
task_queue="tq",
id_conflict_policy=WorkflowIDConflictPolicy.FAIL,
),
wait_for_stage=WorkflowUpdateStage.ACCEPTED,
)
assert err.value.status == RPCStatusCode.INTERNAL
assert err.value.message == "empty details"
assert len(err.value.grpc_status.details) == 0
Loading