slackapi / bolt-python

A framework to build Slack apps using Python

Home Page:https://slack.dev/bolt-python/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Slack_bolt sends 200 to failed events

glebkrapivin opened this issue · comments

commented

I am building an app and encountered a problem where slack would not retry failed requests, because the app sends 200 in response. How do i get the app to send 500 or 404 for the requests my app failed to process, so that they could be retried? Or the only is to use inbox pattern?

Reproducible in fastapi:

from slack_bolt import App, BoltResponse
from slack_bolt.adapter.fastapi import SlackRequestHandler
from slack_bolt.oauth.callback_options import CallbackOptions, FailureArgs, SuccessArgs
from slack_bolt.oauth.oauth_settings import OAuthSettings
from slack_sdk.oauth.installation_store.sqlalchemy import SQLAlchemyInstallationStore
from slack_sdk.oauth.state_store.sqlalchemy import SQLAlchemyOAuthStateStore

installation_store = SQLAlchemyInstallationStore(
    client_id=settings.slack_client_id,
    engine=engine,
    bots_table_name="slack_installation_bots",
    installations_table_name="slack_installation_installations",
)
state_store = SQLAlchemyOAuthStateStore(engine=engine, expiration_seconds=600, table_name="slack_state_store")
app = App(
    signing_secret=settings.slack_signing_secret,
    oauth_settings=OAuthSettings(
        client_id=settings.slack_client_id,
        client_secret=settings.slack_client_secret,
        scopes=settings.slack_oauth_scopes,
        user_scopes=settings.slack_user_scopes,
        redirect_uri=settings.slack_redirect_uri,
        install_path="/api/slack/install",
        redirect_uri_path="/api/slack/oauth_redirect",
        installation_store=installation_store,
        state_store=state_store,
        callback_options=CallbackOptions(success=success, failure=failure),
    ),
)


@router.post("/events")
async def handle_slack_events(req: Request):
    return await app_handler.handle(req)


@app.event("message")
def handle_message(client: WebClient, ack, body, context, event, say):
    raise

The slack_bolt version

slack-bolt==1.18.1
slack_sdk==3.27.0

Python runtime version

3.12

I guess it happens somewhere over here slack_bolt/listener/thread_runner.py" 198L, as auto acknowledgement is True, but the default error handler does nothing

            if not request.lazy_only:
                # start the listener function asynchronously
                def run_ack_function_asynchronously():
                    nonlocal ack, request, response
                    try:
                        self.listener_start_handler.handle(
                            request=request,
                            response=response,
                        )
                        listener.run_ack_function(request=request, response=response)
                    except Exception as e:
                        # The default response status code is 500 in this case.
                        # You can customize this by passing your own error handler.
                        if listener.auto_acknowledgement:
                            self.listener_error_handler.handle(
                                error=e,
                                request=request,
                                response=response,
                            )
                        else:
                            if response is None:
                                response = BoltResponse(status=500)
                            response.status = 500
                            if ack.response is not None:  # already acknowledged
                                response = None
                            self.listener_error_handler.handle(
                                error=e,
                                request=request,
                                response=response,
                            )
                            ack.response = response
                    finally:
                        self.listener_completion_handler.handle(
                            request=request,
                            response=response,
                        )

if i change the line here, then it works as expected

            # await for the completion of ack() in the async listener execution
            while ack.response is None and time.time() - starting_time <= 3:
                time.sleep(0.01)

            if response is None and ack.response is None:
                self.logger.warning(warning_did_not_call_ack(listener_name))
                return None

            if response is None and ack.response is not None:
                response = ack.response
                reponse.status = 500 # HERE IS THE CHANGE
                self._debug_log_completion(starting_time, response)
                return response

            if response is not None:
                return response

Thanks @glebkrapivin for writing in 💯

This seems like a real bug, this issue could be a feature request, did you intend to close it?

commented

@WilliamBergamin

I've read the docs where it says that there is no processing should be done, so i guessed it was the desired behavior of the sdk
For my side i solved it with setting raise_error_for_unhandled_request
and adding something like

@app.error
def handle_error(response):
   response.status = 500

@WilliamBergamin When process_before_response is set to False (the default setting), this is an expected behavior. This is not a bug. The issue reporter might notice it afterwards.

Ah, I posted my above comment at the same timing!

I meant that, when process_before_response is set to False (when you don't pass the argument to App constructor, the value is False by default), the error within your @app.event listener does not affect the HTTP response status. Responding to a request from Slack would be done asynchronously regardless of your listener execution result. If you desire to return a 500 error when your listener fails, you can set process_before_response=True in App constructor. A downside of this approach is that your listener execution must complete within 3 seconds (this is a requirement by Slack server side).

I hope this clarifies. I'm in a different timezone, so if you have further questions on this, other team members like @WilliamBergamin can help you out during my night time.