-
Notifications
You must be signed in to change notification settings - Fork 183
Description
Library Version
5.16.0rc3 @ a1540e9
Describe the Bug
When using an interaction command with AutoDefer set to greater than 0 seconds, it is possible for either it or the command callback to raise an error, due to either side thinking that the interaction hasn't already been deferred/responded to.
This manifests in multiple different ways depending on the timing of the error:
- Message has been sent before AutoDefer starts: OK.
- Message is being sent as AutoDefer starts: OK, but an HTTP 400/404 error is logged.
- This is due AutoDefer trying to defer, as send() has sent a POST response, but has not yet set
ctx.responded(andctx.deferred).
- This is due AutoDefer trying to defer, as send() has sent a POST response, but has not yet set
- Message is being sent as AutoDefer is running: Error message "HTTPException: Interaction has already been acknowledged."
- This is due to send() sending an initial response POST rather than a followup PATCH, as AutoDefer has deferred, but has not yet set
ctx.deferred.
- This is due to send() sending an initial response POST rather than a followup PATCH, as AutoDefer has deferred, but has not yet set
- Message has not yet been sent and AutoDefer is completed: OK.
Additionally, if the command callback calls defer() and AutoDefer has already deferred the message, either an "HTTPException: 400" or "AlreadyDeferred" error is emitted.
The underlying issue is that ctx.deferred and ctx.responded are set after the HTTP request(s), rather than before, which means things like AutoDefer may break.
Steps to Reproduce
The following steps may or may not be reproducible depending on the response time of Discord's HTTP endpoints. In my case, the HTTP request took me in the order of hundreds of ms from my computer, which made it easy for me to reproduce.
- Create a slash command with the following:
1.1. AutoDefer (@auto_defer()) set to some seconds (not 0)
1.2. Call toasyncio.sleep()for the same or close amount of time asauto_defertime
1.3. Call toctx.send()at the end to check if it works - Run the bot and call the command
- Receive a message or an error traceback. Also check for ERROR logs.
Expected Results
Neither AutoDefer nor send() produce an error.
Minimal Reproducible Code
@slash_command(name="test")
@auto_defer(time_until_defer=1.0)
async def test_cmd(ctx: SlashContext):
await asyncio.sleep(1.0)
# Also can produce an error:
# await ctx.defer()
await ctx.send("Pong")Traceback
AutoDefer after send() - No traceback, but error log:
[2025-12-21 23:31:49,462] [ERROR] interactions | POST::https://discord.com/api/v10/interactions/.../.../callback: 400
[2025-12-21 23:31:49,468] [INFO] interactions | Command Called: test with event.ctx.args = [] | event.ctx.kwargs = {}
send() during AutoDefer - Traceback sent as message:
Traceback (most recent call last):
File "...\interactions\client\client.py", line 2028, in __dispatch_interaction
response = await callback
^^^^^^^^^^^^^^
File "...\interactions\client\client.py", line 1896, in _run_slash_command
return await command(ctx, **ctx.kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "...\interactions\models\internal\command.py", line 132, in __call__
await self.call_callback(self.callback, context)
File "...\interactions\models\internal\application_commands.py", line 877, in call_callback
return await self.call_with_binding(callback, ctx, *new_args, **new_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "...\interactions\models\internal\callback.py", line 44, in call_with_binding
return await callback(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "...\test.py", line 31, in test_cmd
await ctx.send("Command called")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "...\interactions\models\internal\context.py", line 586, in send
return await super().send(
^^^^^^^^^^^^^^^^^^^
File "...\interactions\client\mixins\send.py", line 124, in send
message_data = await self._send_http_request(message_payload, files=files or file)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "...\interactions\models\internal\context.py", line 505, in _send_http_request
message_data = await self.client.http.post_initial_response(payload, self.id, self.token, files=files)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "...\interactions\api\http\http_requests\interactions.py", line 150, in post_initial_response
return await self.request(
^^^^^^^^^^^^^^^^^^^
File "...\interactions\api\http\http_client.py", line 478, in request
await self._raise_exception(response, route, result)
File "...\interactions\api\http\http_client.py", line 499, in _raise_exception
raise HTTPException(response, response_data=result, route=route)
interactions.client.errors.HTTPException: HTTPException: 400|Bad Request || Interaction has already been acknowledged.
Checklist
- I have searched the open issues for duplicates.
- I have shown the entire traceback, if possible.
- I have removed my token from display, if visible.
- I have attempted to debug this myself, and I believe this issue is with the library
Additional Information
A pull request will be opened to fix this issue.