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.
On this page
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:
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:
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 broke | chat.command response | Time to the error | What our server saw |
|---|---|---|---|
| Handler returns HTTP 500 | dispatch_failed | 0.6 s | the request, then our 500 |
| Handler returns HTTP 401 (what a failed signature check sends) | dispatch_failed | 0.8 s | the request, then our 401 |
| Handler returns HTTP 302 (a redirect, such as http to https) | dispatch_failed | 0.5 s | the request; Slack did not follow it |
Request URL path wrong (/wrong/path) | dispatch_failed | 0.6 s | a 404 for /wrong/path |
| Local server stopped, tunnel still up (the tunnel answered 502) | dispatch_failed | 0.6 s | nothing |
| Tunnel stopped, so the hostname no longer resolved | dispatch_failed | 0.3 s | nothing |
| Socket Mode turned on, no Socket Mode process running | dispatch_failed | 0.3 s | nothing |
| Handler sleeps 2.5 s, then replies | ok | 3.2 s | the request, reply after 2.5 s |
| Handler sleeps 2.8 s, 3.0 s or 4 s, then replies | operation_timeout | 3.4 s | the 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_failedusually 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(...).
Two more checks when the handler is fast and you still get dispatch_failed:
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.
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
Slack API Pagination: next_cursor, the Last Page and invalid_cursor, Tested
Slack paginates list methods with a cursor: pass response_metadata.next_cursor back as cursor until it is empty. We paged 27 real messages on 2 October 2026 and broke the cursor five ways to see which ones fail.
Slack RTM API Deprecated: What rtm.connect Returns for a New App, and the Socket Mode Fix
A Slack app created today cannot use the RTM API. We called rtm.connect and rtm.start with every token a new app gets on 2 October 2026, tried to request the rtm:stream scope, and ran the same bot over Socket Mode.
Slack Bot Icon and Name Per Message: icon_emoji, icon_url and username, Tested
icon_emoji, icon_url and username only work with the chat:write.customize scope, and Slack ignores them silently without it. We tested every case on 2 October 2026, plus webhooks and the app icon upload limits.