Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Command preparation and sync fails when bot is started on an unmanaged event loop #1138

Open
3 tasks done
Starz0r opened this issue Dec 8, 2023 · 4 comments
Open
3 tasks done
Labels
unconfirmed bug Something might not be working

Comments

@Starz0r
Copy link

Starz0r commented Dec 8, 2023

Summary

When passing a event loop to the bot constructor, any command preparation will fail.

Reproduction Steps

  1. Construct a bot.
  2. Pass in a newly created event loop
  3. Annotate a function for a slash command
  4. Run the bot

Minimal Reproducible Code

from typing import Final
import asyncio
import sys
import disnake

EVLOOP: Final[asyncio.AbstractEventLoop] = asyncio.new_event_loop()
DISCORD_CLIENT: commands.InteractionBot(loop=EVLOOP)

@DISCORD_CLIENT.slash_command()
async def ping(ctx: disnake.ApplicationCommandInteraction) -> None:
    await ctx.send("pong!")

async def main():
    await DISCORD_CLIENT.start("PUT A TOKEN HERE", reconnect=True)

if __name__ == "__main__":
    sys.exit(EVLOOP.run_until_complete(main()))

Expected Results

Command preparation and sync to complete successfully.

Actual Results

Command preparations and sync does not run or finish as the tasks are bound to the wrong event loop.

Intents

None

System Information

- Python v3.9.1-final
- disnake v2.9.1-final
    - disnake importlib.metadata: v2.9.1
- aiohttp v3.9.1
- system info: Windows 10 10.0.19041 AMD64

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.

Additional Context

My personal traceback in question:

INFO:disnake.client:logging in using static token
ERROR:asyncio:Task exception was never retrieved
future: <Task finished name='disnake: app_command_preparation' coro=<InteractionBotBase._prepare_application_commands() done, defined at C:\my_virtualenv\lib\site-packages\disnake\ext\commands\interaction_bot_base.py:879> exception=RuntimeError("Task <Task pending name='disnake: app_command_preparation' coro=<InteractionBotBase._prepare_application_commands() running at C:\\my_virtualenv\\lib\\site-packages\\disnake\\ext\\commands\\interaction_bot_base.py:884>> got Future <Future pending> attached to a different loop")>
Traceback (most recent call last):
  File "C:\my_virtualenv\lib\site-packages\disnake\ext\commands\interaction_bot_base.py", line 884, in _prepare_application_commands
    await self.wait_until_first_connect()
  File "C:\my_virtualenv\lib\site-packages\disnake\client.py", line 1517, in wait_until_first_connect
    await self._first_connect.wait()
  File "C:\python3.9.1\lib\asyncio\locks.py", line 226, in wait
    await fut
RuntimeError: Task <Task pending name='disnake: app_command_preparation' coro=<InteractionBotBase._prepare_application_commands() running at C:\my_virtualenv\lib\site-packages\disnake\ext\commands\interaction_bot_base.py:884>> got Future <Future pending> attached to a different loop
ERROR:asyncio:Task exception was never retrieved
future: <Task finished name='Task-10' coro=<CommonBotBase._fill_owners() done, defined at C:\my_virtualenv\lib\site-packages\disnake\ext\commands\common_bot_base.py:92> exception=RuntimeError("Task <Task pending name='Task-10' coro=<CommonBotBase._fill_owners() running at C:\\my_virtualenv\\lib\\site-packages\\disnake\\ext\\commands\\common_bot_base.py:96>> got Future <Future pending> attached to a different loop")>
Traceback (most recent call last):
  File "C:\my_virtualenv\lib\site-packages\disnake\ext\commands\common_bot_base.py", line 96, in _fill_owners
    await self.wait_until_first_connect()  # type: ignore
  File "C:\my_virtualenv\lib\site-packages\disnake\client.py", line 1517, in wait_until_first_connect
    await self._first_connect.wait()
  File "C:\python3.9.1\lib\asyncio\locks.py", line 226, in wait
    await fut
RuntimeError: Task <Task pending name='Task-10' coro=<CommonBotBase._fill_owners() running at C:\my_virtualenv\lib\site-packages\disnake\ext\commands\common_bot_base.py:96>> got Future <Future pending> attached to a different loop
DEBUG:disnake.http:GET https://discord.com/api/v10/users/@me with None has returned 200
DEBUG:disnake.http:A rate limit bucket has been exhausted (bucket: None:None:/users/@me, retry: 0.001).

The Bad News: Slash commands don't sync, which is unfortunate, but…

The Good News: The bot doesn't crash! Which is good! And if the commands were already created and synced by another working instance, then those commands will still dispatch properly if someone calls them!

NOTE: Using DISCORD_CLIENT.run(...) does not error, but I need to be able to manage my own event loop for this bot, and since it's a probable parameter you can pass it, I'd expect that this would be supported as well?

@Starz0r Starz0r added the unconfirmed bug Something might not be working label Dec 8, 2023
@elenakrittik
Copy link
Contributor

If i remember correctly, disnake uses asyncio.get_event_loop/asyncio.get_running_loop in certain parts, which means that the loop you passed via loop= may be a different loop than the one returned by the above-mentioned functions. Use asyncio.set_event_loop(EVLOOP) before the sys.exit(...) line to ensure the loops match. Not sure whether this is a "bug", though.

@euhake
Copy link

euhake commented Dec 19, 2023

Did you get any solution? I have the same problem, when I use bot.start my commands don't sync

@euhake
Copy link

euhake commented Dec 19, 2023

this here worked for me

import asyncio, disnake
from disnake.ext import commands 

EVLOOP = asyncio.new_event_loop()
asyncio.set_event_loop(EVLOOP)

bot = commands.Bot(
    command_prefix=commands.when_mentioned,
    intents=disnake.Intents.all(),
)

@bot.slash_command(description='test')
async def test(inter: disnake.ApplicationCommandInteraction) -> None:
    await inter.response.send_message('working')

async def main():
    EVLOOP = asyncio.get_event_loop()
    server_task = asyncio.create_task('run your server')
    discord_bot_task = asyncio.create_task(bot.start(''))

    await asyncio.gather(socketio_task, discord_bot_task)

EVLOOP.run_until_complete(main())

@Starz0r
Copy link
Author

Starz0r commented Feb 21, 2024

Took me awhile to get back to this. To be honest, I somewhat forgot until I had to come back to maintaining my bot.

My problem with the library is that it should save whatever event loop it was spawned on and only run tasks on that, which isn't the case. If I need to call a Disnake function in another event loop, I assume that internally it will be shunted off to the correct place, but it isn't. I solved my issue by wrapping calls like channel.send(...) in a Task with the correct event loop attached, and sending that off to asyncio.run_coroutine_threadsafe(...). Personally, I don't think I should need to take these steps, but perhaps this could just be a limitation of asyncio in Python in general?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
unconfirmed bug Something might not be working
Projects
None yet
Development

No branches or pull requests

3 participants