1515from slack_sdk .oauth .state_store .async_state_store import AsyncOAuthStateStore
1616from slack_sdk .web .async_client import AsyncWebClient
1717
18+ from slack_bolt .context .ack .async_ack import AsyncAck
1819from slack_bolt .error import BoltError
1920from slack_bolt .listener .async_listener import AsyncListener , AsyncCustomListener
21+ from slack_bolt .listener .async_listener_error_handler import (
22+ AsyncDefaultListenerErrorHandler ,
23+ AsyncListenerErrorHandler ,
24+ AsyncCustomListenerErrorHandler ,
25+ )
2026from slack_bolt .listener_matcher import builtins as builtin_matchers
2127from slack_bolt .listener_matcher .async_listener_matcher import (
2228 AsyncListenerMatcher ,
@@ -199,6 +205,9 @@ def __init__(
199205
200206 self ._async_middleware_list : List [Union [Callable , AsyncMiddleware ]] = []
201207 self ._async_listeners : List [AsyncListener ] = []
208+ self ._async_listener_error_handler = AsyncDefaultListenerErrorHandler (
209+ logger = self ._framework_logger
210+ )
202211 self ._process_before_response = process_before_response
203212
204213 self ._init_middleware_list_done = False
@@ -256,6 +265,10 @@ def installation_store(self) -> Optional[AsyncInstallationStore]:
256265 def oauth_state_store (self ) -> Optional [AsyncOAuthStateStore ]:
257266 return self ._async_oauth_state_store
258267
268+ @property
269+ def listener_error_handler (self ) -> AsyncListenerErrorHandler :
270+ return self ._async_listener_error_handler
271+
259272 # -------------------------
260273 # standalone server
261274
@@ -325,13 +338,24 @@ async def run_async_listener(
325338 ack = request .context .ack
326339 starting_time = time .time ()
327340 if self ._process_before_response :
328- returned_value = await async_listener .run_ack_function (
329- request = request , response = response
330- )
331- if isinstance (returned_value , BoltResponse ):
332- response = returned_value
333- if ack .response is None and async_listener .auto_acknowledgement :
334- await ack () # automatic ack() call if the call is not yet done
341+ try :
342+ returned_value = await async_listener .run_ack_function (
343+ request = request , response = response
344+ )
345+ if isinstance (returned_value , BoltResponse ):
346+ response = returned_value
347+ if ack .response is None and async_listener .auto_acknowledgement :
348+ await ack () # automatic ack() call if the call is not yet done
349+ except Exception as e :
350+ # The default response status code is 500 in this case.
351+ # You can customize this by passing your own error handler.
352+ if response is None :
353+ response = BoltResponse (status = 500 )
354+ response .status = 500
355+ await self ._async_listener_error_handler .handle (
356+ error = e , request = request , response = response ,
357+ )
358+ ack .response = response
335359
336360 if response is not None :
337361 self ._debug_log_completion (starting_time , response )
@@ -347,33 +371,46 @@ async def run_async_listener(
347371 # start the listener function asynchronously
348372 # NOTE: intentionally
349373 async def run_ack_function_asynchronously (
350- request : AsyncBoltRequest , response : BoltResponse ,
374+ ack : AsyncAck , request : AsyncBoltRequest , response : BoltResponse ,
351375 ):
352376 try :
353377 await async_listener .run_ack_function (
354378 request = request , response = response
355379 )
356380 except Exception as e :
357- # TODO: error handler
358- self ._framework_logger .exception (
359- f"Failed to run listener function (error: { e } )"
381+ # The default response status code is 500 in this case.
382+ # You can customize this by passing your own error handler.
383+ if response is None :
384+ response = BoltResponse (status = 500 )
385+ response .status = 500
386+ if ack .response is not None : # already acknowledged
387+ response = None
388+
389+ await self ._async_listener_error_handler .handle (
390+ error = e , request = request , response = response ,
360391 )
392+ ack .response = response
361393
362394 _f : Future = asyncio .ensure_future (
363- run_ack_function_asynchronously (request , response )
395+ run_ack_function_asynchronously (ack , request , response )
364396 )
365397 self ._framework_logger .debug (f"Async listener: { listener_name } started.." )
366398
367399 # await for the completion of ack() in the async listener execution
368400 while ack .response is None and time .time () - starting_time <= 3 :
369401 await asyncio .sleep (0.01 )
370402
371- if ack .response is not None :
403+ if response is None and ack .response is None :
404+ self ._framework_logger .warning (f"{ listener_name } didn't call ack()" )
405+ return None
406+
407+ if response is None and ack .response is not None :
372408 response = ack .response
373409 self ._debug_log_completion (starting_time , response )
374410 return response
375- else :
376- self ._framework_logger .warning (f"{ listener_name } didn't call ack()" )
411+
412+ if response is not None :
413+ return response
377414
378415 # None for both means no ack() in the listener
379416 return None
@@ -399,6 +436,16 @@ def middleware(self, *args):
399436 AsyncCustomMiddleware (app_name = self .name , func = func )
400437 )
401438
439+ # -------------------------
440+ # global error handler
441+
442+ def error (self , * args ):
443+ if len (args ) > 0 :
444+ func = args [0 ]
445+ self ._async_listener_error_handler = AsyncCustomListenerErrorHandler (
446+ logger = self ._framework_logger , func = func ,
447+ )
448+
402449 # -------------------------
403450 # events
404451
0 commit comments