Back to Blog
Guide

Slack dispatch_failed: 7 Ways We Reproduced It, and the 3-Second Fix

We pointed a slash command at a local server and broke it on purpose: HTTP 500, 401 and 302, a wrong path, a dead tunnel, a stopped server and Socket Mode with no process. Each one gave dispatch_failed. A slow reply gave operation_timeout instead.

Slack Green Team
October 2, 2026
October 2, 2026
4 min read
Share:
slack api
developers
slash commands

dispatch_failed means Slack sent your slash command to the app and did not get a usable reply: the Request URL returned an error status, could not be reached, or the app runs in Socket Mode and no process was connected. We reproduced seven causes on 2 October 2026 with a test app in our own workspace, and each one returned dispatch_failed. A reply that arrives but takes longer than about 3 seconds gives a different error, operation_timeout. The fix for both is the same: return HTTP 200 at once and send the real answer to response_url.

What Slack shows for each failure

Slack web hides the error code for most failures. For an HTTP 500, it posted this, visible only to the person who typed the command, and left the command in the message box:

Slack web: an "Only visible to you" message from Slack that reads "/w1lab failed because the app did not respond. Please try again or contact the app developer." with "/w1lab deploy staging" still in the message box

The error code is in the response to chat.command, the call the Slack client makes when you press Enter. We made the same call from the browser session and got:

{"ok": false, "error": "dispatch_failed"}

A timeout looks different. Slack names the error in the message:

Slack web: "/w1lab failed with the error "operation_timeout"" and the command still in the message box

So if you see the "did not respond" text, look for a failed request. If you see operation_timeout, your handler is working but slow.

The causes we reproduced

Our handler was a small Flask app on localhost:8851, reached through a cloudflared quick tunnel. We changed one thing at a time and ran the command:

What we brokechat.command responseTime to the errorWhat our server saw
Handler returns HTTP 500dispatch_failed0.6 sthe request, then our 500
Handler returns HTTP 401 (what a failed signature check sends)dispatch_failed0.8 sthe request, then our 401
Handler returns HTTP 302 (a redirect, such as http to https)dispatch_failed0.5 sthe request; Slack did not follow it
Request URL path wrong (/wrong/path)dispatch_failed0.6 sa 404 for /wrong/path
Local server stopped, tunnel still up (the tunnel answered 502)dispatch_failed0.6 snothing
Tunnel stopped, so the hostname no longer resolveddispatch_failed0.3 snothing
Socket Mode turned on, no Socket Mode process runningdispatch_failed0.3 snothing
Handler sleeps 2.5 s, then repliesok3.2 sthe request, reply after 2.5 s
Handler sleeps 2.8 s, 3.0 s or 4 s, then repliesoperation_timeout3.4 sthe request; the reply was thrown away

Three things the table shows:

  • • Slack sent each command once. Our log has one request per attempt, with no retries, so a failed command is lost unless the user runs it again.
  • • The time budget is shorter than 3 seconds of your own code. Through the tunnel, a 2.5 s handler passed and a 2.8 s handler failed. Network time counts too.
  • • Socket Mode and a dead URL give the same code. If your app uses Socket Mode, dispatch_failed usually means the process that holds the WebSocket is not running; see our Socket Mode in Python test.
  • For the network causes, open the Request URL from the command's settings in a browser or with curl. If the tunnel or host is down, you will see it there before you look at your code. A free cloudflared or ngrok URL changes every time it restarts, and the slash command keeps pointing at the old one.

Never appear "away" on Slack again

Cloud-based. No downloads. Works 24/7 even when your laptop is off.

The fix: answer first, work later

Return an empty HTTP 200 within the time budget, then post the result to the response_url that came with the command. This is the branch of our test server that did it:

import threading, time
import requests
from flask import Flask, request, make_response

app = Flask(__name__)

def later(url, delay, text):
    time.sleep(delay)                      # the slow work goes here
    r = requests.post(url, json={"response_type": "ephemeral", "text": text}, timeout=10)
    print("response_url:", r.status_code, r.text)

@app.post("/slack/commands")
def commands():
    form = request.form.to_dict()
    threading.Thread(target=later, daemon=True,
                     args=(form["response_url"], 5, f"finished the slow work for '{form.get('text')}'")).start()
    return make_response("", 200)          # the acknowledgement Slack waits for

With a 5-second job, chat.command returned {"ok": true} in 0.6 s. Five seconds later our server printed:

response_url: 200 ok

and Slack showed "finished the slow work for 'raw ack later'" to the user. For a button click, the response_url took 5 posts and expired after 30 minutes in our interactivity tests; plan for the same limits here. In Bolt for Python the same pattern is ack() first, then respond(...). Every field of the command payload, and what each reply body shows, is in our slash command payload test.

Two more checks when the handler is fast and you still get dispatch_failed:

  • • The handler must answer the exact path in the Request URL. Our wrong-path test reached the server and still failed, because Flask returned 404.
  • • Any status outside 2xx failed in our tests: 500, 404, 401 and even a 302 redirect. A 204 with no body passed. If your signature check rejects the request with a 401, the user sees the same "did not respond" message, so log the status your server returns, not only the requests it receives.
  • FAQ

    Does Slack retry a failed slash command?

    No. In our tests each failed command reached the server at most once. Events API deliveries are different: Slack retries those three times; see our retry timings.

    What does the user see while the slow work runs?

    Nothing. With the empty 200, chat.command returned {"ok": true}, the command left the message box, and no message appeared until our server posted to response_url 5 seconds later. If the work takes longer than a second or two, post a short "working on it" reply first, or return it as the body of the 200.

    Always Active

    Stop Jiggling Your Mouse.

    Join hundreds of remote workers who never worry about their Slack status. Set it up once, stay green forever.

    Related Articles

    Guide

    How to See All Your DMs in Slack, Including the Hidden Ones

    The DMs tab lists your direct messages with people. App DMs, Slackbot and DMs with deleted accounts sit elsewhere. We compared the tab with the full list the Slack API returned.

    Slack Green Team
    Guide

    Slack Images Not Showing? 5 Causes We Reproduced

    We uploaded images to a Slack test channel and then switched each image setting off, ran /collapse, and posted from a bot that was not in the channel. Five causes, each reproduced.

    Slack Green Team
    Guide

    How to Message Yourself on Slack (and What It Notifies)

    Your own name under Direct messages opens a private DM with yourself. We sent it a note, a file, a scheduled message and a reminder, and checked which ones raised a badge.

    Slack Green Team