11# (C) 2024 GoodData Corporation
22import base64
33import traceback
4- from typing import Callable , Optional , Union
4+ from typing import Callable , Optional , Union , cast
55
66import orjson
77import pyarrow .flight
88
99from gooddata_flight_server .errors .error_code import ErrorCode
1010
11+ _ERROR_INFO_MAX_MSG = 256
12+ _ERROR_INFO_MAX_DETAIL = 512
13+
14+
15+ def _truncate_str_value (val : Optional [str ], max_len : int ) -> Optional [str ]:
16+ if val is None :
17+ return None
18+
19+ if len (val ) <= max_len :
20+ return val
21+
22+ # no big deal that the actual max length is slightly exceeded
23+ # all this truncating happens because ErrorInfo is eventually
24+ # passed via gRPC headers which have 16k hard limit
25+ return val [:max_len ] + " [truncated]"
26+
1127
1228class ErrorInfo :
1329 """
@@ -22,15 +38,15 @@ def __init__(
2238 body : Optional [bytes ] = None ,
2339 code : int = 0 ,
2440 ) -> None :
25- self ._msg = msg
26- self ._detail : Optional [str ] = detail
41+ self ._msg = cast ( str , _truncate_str_value ( msg , _ERROR_INFO_MAX_MSG ))
42+ self ._detail : Optional [str ] = _truncate_str_value ( detail , _ERROR_INFO_MAX_DETAIL )
2743 self ._body : Optional [bytes ] = body
2844 self ._code : int = code
2945
3046 @property
3147 def msg (self ) -> str :
3248 """
33- :return: human readable error message
49+ :return: human- readable error message
3450 """
3551 return self ._msg
3652
@@ -60,26 +76,36 @@ def with_msg(self, msg: str) -> "ErrorInfo":
6076 """
6177 Updates error message.
6278
63- :param msg: new message
79+ :param msg: new message, up to 256 characters; will be truncated if the limit is exceeded
6480 :return: self, for call chaining sakes
6581 """
66- self ._msg = msg
82+ self ._msg = cast (str , _truncate_str_value (msg , _ERROR_INFO_MAX_MSG ))
83+
6784 return self
6885
6986 def with_detail (self , detail : Optional [str ] = None ) -> "ErrorInfo" :
7087 """
7188 Updates or resets the error detail.
7289
73- :param detail: detail to set; if None, the detail stored in the meta will be removed; default is None
90+ :param detail: detail to set; if None, the detail stored in the meta will be removed; default is None;
91+ detail can be up to 512 characters; will be truncated if the limit is exceeded
7492 :return: self, for call chaining sakes
7593 """
76- self ._detail = detail
94+ self ._detail = _truncate_str_value (detail , _ERROR_INFO_MAX_DETAIL )
95+
7796 return self
7897
7998 def with_body (self , body : Optional [Union [bytes , str ]]) -> "ErrorInfo" :
8099 """
81100 Updates or resets the error body.
82101
102+ IMPORTANT: the ErrorInfo (and thus the contents of `body`) are passed out via FlightError.extra_info
103+ property. The Flight RPC implementations pass the `extra_info` via gRPC headers. In turn, the gRPC headers
104+ do have size limit. Keep this in mind when designing the value of `body`.
105+
106+ If you set body that is too large, you will run into problems like this:
107+ https://github.com/grpc/grpc/issues/37852.
108+
83109 :param body: body to set; if None, the body stored in the meta will be removed; default is None
84110 :return: self, for call chaining sakes
85111 """
0 commit comments