-
Notifications
You must be signed in to change notification settings - Fork 305
feat(sdk): normalize gRPC transport error types (no retry behavior ch… #3027
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “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? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
aa9b3b2
f939275
3ce054e
8337b3b
14f0230
0efb24e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,9 +1,13 @@ | ||||||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||||||
| from typing import Literal, cast, overload | ||||||||||||||||||||||||||||||
| from typing import Literal, cast, overload, Callable, TypeVar | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| import grpc | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| from hatchet_sdk.config import ClientConfig | ||||||||||||||||||||||||||||||
| from hatchet_sdk.exceptions import HatchetError | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| T = TypeVar("T") | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| @overload | ||||||||||||||||||||||||||||||
|
|
@@ -26,6 +30,7 @@ def new_conn(config: ClientConfig, aio: bool) -> grpc.Channel | grpc.aio.Channel | |||||||||||||||||||||||||||||
| root = f.read() | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| credentials = grpc.ssl_channel_credentials(root_certificates=root) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| elif config.tls_config.strategy == "mtls": | ||||||||||||||||||||||||||||||
| assert config.tls_config.root_ca_file | ||||||||||||||||||||||||||||||
| assert config.tls_config.key_file | ||||||||||||||||||||||||||||||
|
|
@@ -59,13 +64,9 @@ def new_conn(config: ClientConfig, aio: bool) -> grpc.Channel | grpc.aio.Channel | |||||||||||||||||||||||||||||
| ("grpc.default_compression_algorithm", grpc.Compression.Gzip), | ||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Set environment variable to disable fork support. Reference: https://github.com/grpc/grpc/issues/28557 | ||||||||||||||||||||||||||||||
| # When steps execute via os.fork, we see `TSI_DATA_CORRUPTED` errors. | ||||||||||||||||||||||||||||||
| # needs to be the string "True" or "False" | ||||||||||||||||||||||||||||||
| os.environ["GRPC_ENABLE_FORK_SUPPORT"] = str(config.grpc_enable_fork_support) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if config.grpc_enable_fork_support: | ||||||||||||||||||||||||||||||
| # See discussion: https://github.com/hatchet-dev/hatchet/pull/2057#discussion_r2243233357 | ||||||||||||||||||||||||||||||
| os.environ["GRPC_POLL_STRATEGY"] = "poll" | ||||||||||||||||||||||||||||||
|
Comment on lines
67
to
70
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if config.tls_config.strategy == "none": | ||||||||||||||||||||||||||||||
|
|
@@ -88,3 +89,61 @@ def new_conn(config: ClientConfig, aio: bool) -> grpc.Channel | grpc.aio.Channel | |||||||||||||||||||||||||||||
| grpc.Channel | grpc.aio.Channel, | ||||||||||||||||||||||||||||||
| conn, | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # ------------------------------------------------------------------- | ||||||||||||||||||||||||||||||
| # gRPC execution + transport error normalization (no behavior change) | ||||||||||||||||||||||||||||||
| # ------------------------------------------------------------------- | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def _execute_grpc_call( | ||||||||||||||||||||||||||||||
| fn: Callable[..., T], | ||||||||||||||||||||||||||||||
| *args, | ||||||||||||||||||||||||||||||
| **kwargs, | ||||||||||||||||||||||||||||||
| ) -> T: | ||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||
| Executes a gRPC stub call and normalizes transport-level errors. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Does NOT change retry behavior. | ||||||||||||||||||||||||||||||
| Only translates grpc.RpcError into clearer SDK-level exceptions. | ||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||
| return fn(*args, **kwargs) | ||||||||||||||||||||||||||||||
| except grpc.RpcError as e: | ||||||||||||||||||||||||||||||
| raise _translate_grpc_error(e) from e | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def _translate_grpc_error(e: grpc.RpcError) -> Exception: | ||||||||||||||||||||||||||||||
|
Comment on lines
+106
to
+110
|
||||||||||||||||||||||||||||||
| except grpc.RpcError as e: | |
| raise _translate_grpc_error(e) from e | |
| def _translate_grpc_error(e: grpc.RpcError) -> Exception: | |
| except (grpc.RpcError, grpc.aio.AioRpcError) as e: | |
| raise _translate_grpc_error(e) from e | |
| def _translate_grpc_error( | |
| e: grpc.RpcError | grpc.aio.AioRpcError, | |
| ) -> Exception: |
Copilot
AI
Feb 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type annotation should accept both grpc.RpcError and grpc.aio.AioRpcError to be consistent with the codebase pattern (see admin.py lines 334, 402, 435). Suggest: def _translate_grpc_error(e: grpc.RpcError | grpc.aio.AioRpcError) -> Exception:
| def _translate_grpc_error(e: grpc.RpcError) -> Exception: | |
| def _translate_grpc_error(e: grpc.RpcError | grpc.aio.AioRpcError) -> Exception: |
Copilot
AI
Feb 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The base class HatchetError does not exist. This class should inherit from Exception instead, following the pattern used by all other exceptions in the codebase (e.g., InvalidDependencyError, NonRetryableException, DedupeViolationError in exceptions.py).
Copilot
AI
Feb 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new exception classes (GRPCTransportError, GRPCTimeoutError, GRPCUnavailableError) are defined in connection.py, but all other SDK exception classes are defined in exceptions.py (e.g., InvalidDependencyError, DedupeViolationError, TaskRunError, etc.). For consistency with the existing codebase structure, these exception classes should be moved to exceptions.py where all other SDK exceptions are centralized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The class
HatchetErrordoes not exist inhatchet_sdk.exceptions. This import will fail at runtime. Based on the existing exception conventions in the codebase (seeexceptions.pylines 6-173), all custom exceptions inherit directly fromExceptionorValueError. The import should be removed andGRPCTransportErrorshould inherit fromExceptioninstead ofHatchetError.