You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何排查Slack Bolt触发Modal时出现的404错误

问题:Slack Modal弹窗唤起失败(404错误)

问题背景

正在开发Slack应用的Modal弹窗功能,点击消息内的按钮唤起弹窗,使用block builder构建JSON结构。发送普通消息功能正常,但唤起弹窗时出现404错误,弹窗无法正常打开。

现有代码

Bolt应用处理代码

app.action('watch-skill-video', async ({ ack, action, client, say, respond }) => {

  ack()
  const msg = await watchLayoutBuilder('some value', 'someother value')
  console.log('Message - ' + msg)
  await respond(JSON.parse(msg))

});

生成的Modal JSON结构

{
  "title": {
    "type": "plain_text",
    "text": "Abc"
  },
  "submit": {
    "type": "plain_text",
    "text": "Submit"
  },
  "blocks": [
    {
      "text": {
        "type": "plain_text",
        "text": "Dummy"
      },
      "type": "button"
    }
  ],
  "type": "modal"
}

错误信息

[ERROR]  bolt-app UnknownError: Request failed with status code 404
    at asCodedError (/Users/rajeshghosh/Projects/pwa-slack/node_modules/@slack/bolt/dist/errors.js:35:12)
    at App.handleError (/Users/rajeshghosh/Projects/pwa-slack/node_modules/@slack/bolt/dist/App.js:608:57)
    at App.processEvent (/Users/rajeshghosh/Projects/pwa-slack/node_modules/@slack/bolt/dist/App.js:593:25)
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
    at async SocketModeClient.<anonymous> (/Users/rajeshghosh/Projects/pwa-slack/node_modules/@slack/bolt/dist/receivers/SocketModeReceiver.js:126:17) {
  code: 'slack_bolt_unknown_error',
  original: Error: Request failed with status code 404
      at createError (/.../node_modules/@slack/bolt/node_modules/axios/lib/core/createError.js:16:15)
      at settle (/.../node_modules/@slack/bolt/node_modules/axios/lib/core/settle.js:17:12)
      at IncomingMessage.handleStreamEnd (/.../node_modules/@slack/bolt/node_modules/axios/lib/adapters/http.js:322:11)
      at IncomingMessage.emit (events.js:412:35)
      at IncomingMessage.emit (domain.js:475:12)
      at endReadableNT (internal/streams/readable.js:1334:12)
      at processTicksAndRejections (internal/process/task_queues.js:82:21) {
    config: {
      transitional: [Object],
      adapter: [Function: httpAdapter],
      transformRequest: [Array],
      transformResponse: [Array],
      timeout: 0,
      xsrfCookieName: 'XSRF-TOKEN',
      xsrfHeaderName: 'X-XSRF-TOKEN',
      maxContentLength: -1,
      maxBodyLength: -1,
      validateStatus: [Function: validateStatus],
      headers: [Object],
      proxy: false,
      method: 'post',
      url: 'https://hooks.slack.com/actions/TFTS42ALT/3855892288214/7lmakyKKBAvk0paJguVzxoih',
      data: '{"title":{"type":"plain_text","text":"Abc"},"submit":{"type":"plain_text","text":"Submit"},"blocks":[{"text":{"type":"plain_text","text":"Dummy"},"type":"button"}],"type":"modal"}'
    },
    request: ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: false,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: true,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      _contentLength: null,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      socket: [TLSSocket],
      _header: 'POST /actions/TFTS42ALT/3855892288214/7lmakyKKBAvk0paJguVzxoih HTTP/1.1\r\n' +
        'Accept: application/json, text/plain, */*\r\n' +
        'Content-Type: application/json\r\n' +
        'User-Agent: axios/0.26.1\r\n' +
        'Content-Length: 179\r\n' +
        'Host: hooks.slack.com\r\n' +
        'Connection: close\r\n' +
        '\r\n',
      _keepAliveTimeout: 0,
      _onPendingData: [Function: noopPendingOutput],
      agent: [Agent],
      socketPath: undefined,
      method: 'POST',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      path: '/actions/TFTS42ALT/3855892288214/7lmakyKKBAvk0paJguVzxoih',
      _ended: true,
      res: [IncomingMessage],
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      host: 'hooks.slack.com',
      protocol: 'https:',
      _redirectable: [Writable],
      [Symbol(kCapture)]: false,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kOutHeaders)]: [Object: null prototype]
    },
    response: {
      status: 404,
      statusText: 'Not Found',
      headers: [Object],
      config: [Object],
      request: [ClientRequest],
      data: [Object]
    },
    isAxiosError: true,
    toJSON: [Function: toJSON]
  }
}
[ERROR]   An unhandled error occurred while Bolt processed (type: block_actions, error: Error: Request failed with status code 404)

问题分析与解决方案

核心问题

使用respond()方法发送Modal是错误的,respond()仅用于回复触发动作的消息(如修改原消息或发送新消息到频道),它的请求目标是Slack Webhook URL;而Modal需要通过Slack API的views.open方法唤起,不能通过Webhook实现。

修正代码

将respond()替换为client.views.open(),并传入正确的参数结构:

app.action('watch-skill-video', async ({ ack, action, client }) => {
  await ack();
  
  const modal = await watchLayoutBuilder('some value', 'someother value');
  const modalJson = JSON.parse(modal);
  
  await client.views.open({
    trigger_id: action.trigger_id, // 必须从触发动作的action对象中获取,有效期3秒
    view: modalJson
  });
});

关键说明

  • trigger_id是唤起Modal的必填参数,必须从触发按钮的action对象中获取,且仅在触发后的3秒内有效。
  • client.views.open()是Bolt官方提供的唤起Modal的标准API方法,会正确调用Slack的API端点,而非Webhook。
  • 确保Slack应用已配置所需权限:包括commands、users:read基础权限,以及views:open权限。

内容的提问来源于stack exchange,提问作者Rajesh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 10:15:39