slackapi / bolt-js

A framework to build Slack apps using JavaScript
https://slack.dev/bolt-js
MIT License
2.74k stars 393 forks source link

Issue when "respond" method is called #1261

Closed francozini2 closed 2 years ago

francozini2 commented 2 years ago

Description

Hi everyone!

I am using bolt JS to develop an app and I want to handle an action event.

I am sending a message to the users, and they can respond by clicking a button. When I receive the answer, I am using the "respond" method.

I have the following code:

app.action("action_id", ack, myMiddleware) 

. . .

const myMiddleware = async ({ body, respond, context, next = null }) => {
    await respond(blockMessage)
    return await next()
}

where the blockMessage is the following JSON:

{
  "blocks": [
    {
      "type": "image",
      "title": {
        "type": "plain_text",
        "text": "My message",
        "emoji": true
      },
      "image_url": "<IMAGE_URL>",
      "alt_text": "my_message"
    }
  ]
}

If the user left pass a few seconds everything works fine, but, when the user tap immediately the button, the "respond" method returns an error message (Request failed with status code 404)

{
    "code":"slack_bolt_unknown_error",
    "original":{
        "message":"Request failed with status code 404",
        "name":"Error",
        "stack":"Error: Request failed with status code 404\n    at createError
            (/slackApp/node_modules/@slack/bolt/node_modules/axios/lib/core/createError.js:16:15)\n    at settle
            (/slackApp/node_modules/@slack/bolt/node_modules/axios/lib/core/settle.js:17:12)\n    at IncomingMessage.handleStreamEnd 
            (/slackApp/node_modules/@slack/bolt/node_modules/axios/lib/adapters/http.js:269:11)\n    at IncomingMessage.emit
            (events.js:215:7)\n    at endReadableNT 
            (_stream_readable.js:1183:12)\n    at processTicksAndRejections 
            (internal/process/task_queues.js:80:21)",
        "config":{
            "URL":"<HOOK_URL>",
            "method":"post",
            "data":"{\"blocks\":[{\"type\":\"image\",\"title\":{\"type\":\"plain_text\",\"text\":\"Text\",\"emoji\":true},\"image_url\":\"<IMAGE_URL>\",\"alt_text\":\"text\"}]}",
            "headers":{
                "Accept":"application/json, text/plain, */*",
                "Content-Type":"application/json",
                "User-Agent":"axios/0.21.4",
                "Content-Length":188
            },
            "proxy":false,
            "transformRequest":[null],
            "transformResponse":[null],
            "timeout":0,
            "xsrfCookieName":"XSRF-TOKEN",
            "xsrfHeaderName":"X-XSRF-TOKEN",
            "maxContentLength":-1,
            "maxBodyLength":-1,
            "transitional":{
                "silentJSONParsing":true,
                "forcedJSONParsing":true,
                "clarifyTimeoutError":false
            }
        }
    }
}

Is there any workaround for these cases?

Best regards!!

What type of issue is this? (place an x in one of the [ ])

Requirements (place an x in each of the [ ])


Reproducible in:

package version: 3.8.0

node version: 12.13.0

OS version(s): All platforms (Android, iOS, web(tested on Linux and Mac SO))

misscoded commented 2 years ago

Hi @francozini2! I've gone ahead and tried to reproduce the issue you've outlined above.

The only circumstance under which I receive a 404 is when the image_url is incorrect or missing. I noticed that in the error log you've provided, image_url is set to "<IMAGE_URL>". This needs to point to a valid image, else it will fail.

I'm not sure if the above code is exactly as you have it written in your application, but can you have a look at the logic around the image URL and ensure that it's being provided to the payload?

francozini2 commented 2 years ago

Hi @misscoded ! thanks for your answer! That makes sense and I think that this is the problem. Let me check

francozini2 commented 2 years ago

Hi @misscoded ! I tried with another payload and the problem persists. I tried with this payload: { "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": ":ghost:" } } ] } The only difference is in this case I don't receive an error. The button shows the progress but nothing happen. After try two or three times works fine, Do the respond method have a delay or something like that?

misscoded commented 2 years ago

Hmm. I can't replicate the problem you're experiencing, so the cause must be elsewhere.

Would you mind trying the following approach and letting me know if this solves the issue?

const actionHandler = async ({ ack, respond })  => {
  await ack();
  const blocks = [
    {
      "type": "image",
      "title": {
          "type": "plain_text",
          "text": "My message",
          "emoji": true
      },
      "image_url": "https://assets3.thrillist.com/v1/image/1682388/size/tl-horizontal_main.jpg",
      "alt_text": "my_message"
      }
  ];

  respond({ blocks });
};

app.action("action_id", actionHandler);
francozini2 commented 2 years ago

Hi @misscoded ! Let me show you a video with the behaviour of the bug

https://user-images.githubusercontent.com/14354024/148555941-837277b3-4cd5-4d14-b16e-5408092700c1.mov

misscoded commented 2 years ago

I see. That response indicates that the click (action) event was sent but your application didn't handle it.

If you click the first icon (😃 ) a second time through, does it work as expected? Or does it also fail? Is that the only button that works as expected?

The failure seen here occurs when the button uses an action_id that does not have a corresponding listener in the app. Each button needs to feature a unique action_id and that action_id needs to be handled by a listener.

If you'd like to utilize a single listener to handle a group of action_ids that feature a common pattern (in the case below, starting with mood-), you could do something like the following:

const actionCallback = async ({ ack, action, respond })  => {
  await ack();
  const emoji = action.text.text;
  respond(`Your mood is ${emoji}.`);
};

app.action(/^mood-.*/, actionCallback);

app.message('hi', async ({ say }) => {
  const blocks = [
    {
      "type": "actions",
      "elements": [
        {
          "type": "button",
          "text": {
          "type": "plain_text",
            "text": ":smiley:",
             "emoji": true
           },
          "value": "mood-0",
      "action_id": "mood-0"
    },
        {
         "type": "button",
         "text": {
            "type": "plain_text",
        "text": ":slightly_smiling_face:",
        "emoji": true
          },
          "value": "mood-1",
          "action_id": "mood-1"
        },
    ];
  say({ blocks });
});

Without seeing the code behind what you're doing here, it's difficult to diagnose what's wrong. Let us know if the above suggestion works and, if not, if you could provide more of the logic being used in your app, that would be helpful.

francozini2 commented 2 years ago

Hi @misscoded!

I found on the developer console this image

The details of the requests are:

curl 'https://testmoralepalv2group.slack.com/api/blocks.actions?_x_id=80381846-1641594289.680&_x_csid=VVwQyZg_QYo&slack_route=T02N4EH32TH&_x_version_ts=1641508046&_x_gantry=true&fp=46' \ -H 'authority: testmoralepalv2group.slack.com' \ -H 'sec-ch-ua: "Chromium";v="94", "Google Chrome";v="94", ";Not A Brand";v="99"' \ -H 'sec-ch-ua-mobile: ?0' \ -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36' \ -H 'sec-ch-ua-platform: "Linux"' \ -H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary1KLBSjh5YwMxfjPU' \ -H 'accept: */*' \ -H 'origin: https://app.slack.com' \ -H 'sec-fetch-site: same-site' \ -H 'sec-fetch-mode: cors' \ -H 'sec-fetch-dest: empty' \ -H 'accept-language: en,es-419;q=0.9,es;q=0.8' \ -H 'cookie: __qca=P0-1047650120-1625929916064; _lc2_fpi=e00b11ac9c9b--01fa8fk7z5acmfrzht3j9v7j7c; b=.48x2pgd8manhym7sut488iep2; shown_ssb_redirect_page=1; shown_download_ssb_modal=1; show_download_ssb_banner=1; no_download_ssb_banner=1; optimizelyEndUserId=oeu1626091195278r0.8372469978639243; _cs_c=1; OptanonAlertBoxClosed=2021-08-26T00:04:05.962Z; _rdt_uuid=1631838881858.65a49a2a-eda2-49b8-a29f-18b4013ab48f; c={"digital_first_lightning_strike_custacq":1}; _gcl_au=1.1.1663044081.1634078435; app_submission_first_time_modal_dismissed=1; _ga=GA1.3.422605571.1625929889; __pdst=8615b1723fab43e9ad47848ba73814c5; _li_dcdm_c=.slack.com; d=y8UBSMZAquYyhOLJcQVQ0cyRvYh25T4X9W3UQdwuKOJ3uCVuyaDvoCh3y%2BPKWKSraVZ2ntkHt3fvtxUBTGycNOfMn0EDBfb9oc00Fi0YqbhE1gcdG1%2BplpJFkLh4Gj5UIh3H6CYN6CT%2BIj6VVhXLM4VqiKPgpcZCvP0rcf5heKfLOvlUHtAogk9mtw%3D%3D; d-s=1641430850; lc=1641430850; utm=%7B%22utm_source%22%3A%22in-prod%22%2C%22utm_medium%22%3A%22inprod-btn_app_install-index-c%22%7D; _cs_id=760fac1a-3f1e-ab77-c9a5-8e6cb8ba0ce7.1627941680.176.1641431571.1641430872.1.1662105680450; PageCount=83; OptanonConsent=isIABGlobal=false&datestamp=Thu+Jan+06+2022+23%3A28%3A56+GMT-0300+(Argentina+Standard+Time)&version=6.22.0&hosts=&consentId=ec20aa54-e9ee-4170-b719-7c9e64f1a596&interactionCount=1&landingPath=NotLandingPage&groups=C0004%3A1%2CC0002%3A1%2CC0003%3A1%2CC0001%3A1&AwaitingReconsent=false&geolocation=AR%3BS&isGpcEnabled=0; _ga_QTJQME5M5D=GS1.1.1641521780.162.1.1641522536.0; _ga=GA1.2.422605571.1625929889; _gid=GA1.2.1251005390.1641592204' \ --data-raw $'------WebKitFormBoundary1KLBSjh5YwMxfjPU\r\nContent-Disposition: form-data; name="service_id"\r\n\r\nB02NK4YC9NX\r\n------WebKitFormBoundary1KLBSjh5YwMxfjPU\r\nContent-Disposition: form-data; name="service_team_id"\r\n\r\nT02N4EH32TH\r\n------WebKitFormBoundary1KLBSjh5YwMxfjPU\r\nContent-Disposition: form-data; name="actions"\r\n\r\n[{"action_id":"mood_very_happy","block_id":"mood_actions_first","text":{"type":"plain_text","text":":grinning:","emoji":true},"type":"button"}]\r\n------WebKitFormBoundary1KLBSjh5YwMxfjPU\r\nContent-Disposition: form-data; name="container"\r\n\r\n{"type":"message","message_ts":"1641593772.018500","channel_id":"D02NMBWKC6Q","is_ephemeral":false}\r\n------WebKitFormBoundary1KLBSjh5YwMxfjPU\r\nContent-Disposition: form-data; name="client_token"\r\n\r\nweb-1641594289680\r\n------WebKitFormBoundary1KLBSjh5YwMxfjPU\r\nContent-Disposition: form-data; name="state"\r\n\r\n{"values":{}}\r\n------WebKitFormBoundary1KLBSjh5YwMxfjPU\r\nContent-Disposition: form-data; name="token"\r\n\r\nxoxc-2752493104935-2779766650497-2767138170498-211997d4f9352f5948b63aae615cba46e31170256a10f1f36fd4e7016076ed0d\r\n------WebKitFormBoundary1KLBSjh5YwMxfjPU\r\nContent-Disposition: form-data; name="_x_reason"\r\n\r\ndispatch_action_to_developer\r\n------WebKitFormBoundary1KLBSjh5YwMxfjPU\r\nContent-Disposition: form-data; name="_x_mode"\r\n\r\nonline\r\n------WebKitFormBoundary1KLBSjh5YwMxfjPU\r\nContent-Disposition: form-data; name="_x_sonic"\r\n\r\ntrue\r\n------WebKitFormBoundary1KLBSjh5YwMxfjPU--\r\n' \ --compressed

curl 'https://slack.com/beacon/error' \ -H 'authority: slack.com' \ -H 'sec-ch-ua: "Chromium";v="94", "Google Chrome";v="94", ";Not A Brand";v="99"' \ -H 'sec-ch-ua-mobile: ?0' \ -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36' \ -H 'sec-ch-ua-platform: "Linux"' \ -H 'content-type: application/x-www-form-urlencoded' \ -H 'accept: */*' \ -H 'origin: https://app.slack.com' \ -H 'sec-fetch-site: same-site' \ -H 'sec-fetch-mode: cors' \ -H 'sec-fetch-dest: empty' \ -H 'accept-language: en,es-419;q=0.9,es;q=0.8' \ --data-raw 'description=Error%3A%2080381846-1641594289.680%20blocks.actions%20not%20ok%3A%20post_error&version=1641508046&version_sha=80381846ec2de98f4b6d8e7f8b1d8ad417d0e93c&error_json=%7B%22subtype%22%3A%22missing_subtype%22%2C%22message%22%3A%22hashed%20timestamp%20blocks.actions%20not%20ok%3A%20post_error%22%2C%22stack%22%3A%22post_error%3A%2080381846-1641594289.680%20blocks.actions%20not%20ok%3A%20post_error%5Cn%20%20%20%20at%20slack_api_request_SlackApiRequest._onNotOk%20(https%3A%2F%2Fa.slack-edge.com%2Fbv1-9%2Fgantry-shared.3fc6146.min.js%3FcacheKey%3Dgantry-1641508046%3A1%3A39468)%5Cn%20%20%20%20at%20slack_api_request_SlackApiRequest.resolve%20(https%3A%2F%2Fa.slack-edge.com%2Fbv1-9%2Fgantry-shared.3fc6146.min.js%3FcacheKey%3Dgantry-1641508046%3A1%3A39211)%5Cn%20%20%20%20at%20slack_api_request_SlackApiRequest._onLoad%20(https%3A%2F%2Fa.slack-edge.com%2Fbv1-9%2Fgantry-shared.3fc6146.min.js%3FcacheKey%3Dgantry-1641508046%3A1%3A739215)%22%7D&team=T02N4EH32TH&user=U02NXNJK4EM&session_id=fad362a2-1cb2-4ac7-be1e-bc850838ce22&visitor_cookie=.48x2pgd8manhym7sut488iep2&sub_app_name=client&tracing_enabled_for_session=false' \ --compressed

I have handlers for all action ids image

This error occurs immediately after the button is pressed and it is a random error and is not related to the ack.

misscoded commented 2 years ago

Again, assuming we are still referring to the warning icon that appears after the button click:

If you click the first icon (😃 ) a second time through, does it work as expected? Each and every time? Or does it also fail? Is that the only button that always works as expected?

Beyond the above questions, I'd recommend tracing the action event through your middleware to see if it's being caught by ack, and then consequently the validator function you appear to have in place. Does each action get to the point you'd expect it to? What is the difference between those that do and don't?

francozini2 commented 2 years ago

To simplify I changed the code and now I am trying exactly with this code:

slackApp.message('hi', async ({ say }) => {
    const blocks = [
        {
          "type": "actions",
          "elements": [
            {
              "type": "button",
              "text": {
                "type": "plain_text",
                "text": ":smiley:",
                "emoji": true
              },
              "action_id": "mood_smiley"
            }
          ]
        }
    ];
    say({ blocks });
});

const actionCallback = async ({ ack, action, respond })  => {
    console.log("actionCallback")
    await ack();
    const emoji = action.text.text;
    respond(`Your mood is ${emoji}.`);
};

slackApp.action(new RegExp("^mood_[a-zA-Z0-9_]+"), actionCallback)

I recorded the screen running this code and the result was:

https://user-images.githubusercontent.com/14354024/148657551-bab27914-1ee8-4189-ade2-28b241a4623f.mp4

Notice that the emoji the first time fails, but, after that works, then, works a few times and at the end of the video fails again.

Like as you said, I am tracing the action event but never execute the log sentence console.log("actionCallback")

francozini2 commented 2 years ago

Hi @misscoded! do you have any idea of what can be happening here? Is there a way to track the request from the slack client to our server? Maybe we are losing the request somewhere in the middle and that's why Slack shows us an error

seratch commented 2 years ago

@francozini2

Like as you said, I am tracing the action event but never execute the log sentence console.log("actionCallback")

This indicates that the app might NOT be handling the requests (all of them or some of them). How about doing some tests with a brand-new app? The Request URL may have some issues and the situation might be confusing you.

Is there a way to track the request from the slack client to our server?

The only thing that we can suggest is to check the HTTP server access logs on your end. There is no functionality on the Slack app admin page side to check the logs as of today.

I hope this helps!

github-actions[bot] commented 2 years ago

👋 It looks like this issue has been open for 30 days with no activity. We'll mark this as stale for now, and wait 10 days for an update or for further comment before closing this issue out. If you think this issue needs to be prioritized, please comment to get the thread going again! Maintainers also review issues marked as stale on a regular basis and comment or adjust status if the issue needs to be reprioritized.

github-actions[bot] commented 2 years ago

As this issue has been inactive for more than one month, we will be closing it. Thank you to all the participants! If you would like to raise a related issue, please create a new issue which includes your specific details and references this issue number.