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

Bot can't enter the ready state in voice channels #10356

Open
SOLR4189 opened this issue Jun 19, 2024 · 3 comments
Open

Bot can't enter the ready state in voice channels #10356

SOLR4189 opened this issue Jun 19, 2024 · 3 comments

Comments

@SOLR4189
Copy link

Which package is this bug report for?

discord.js

Issue description

We have noticed that our bot gets the timeout error ~30-40 times a day (against 60-70 successful connections):

Error stack: AbortError: The operation was aborted
    at abortListener (node:events:974:14)
    at AbortSignal.<anonymous> (node:events:1010:47)
    at AbortSignal.[nodejs.internal.kHybridDispatch] (node:internal/event_target:647:20)
    at AbortSignal.dispatchEvent (node:internal/event_target:589:26)
    at abortSignal (node:internal/abort_controller:283:10)
    at AbortController.abort (node:internal/abort_controller:314:5)
    at Timeout.<anonymous> (C:\Users\USER\OneDrive\Documents\Git\my-app\node_modules\@discordjs\voice\dist\index.js:2504:39)
    at listOnTimeout (node:internal/timers:559:17)
    at processTimers (node:internal/timers:502:7)

It happens when the bot joins to the channel and tries to enter to the READY state.
Today we succeeded to reproduce it in our working laptop with DEBUG mode:

For failed entering we see the next log:

[2024-06-18T12:26:40.484]  signalling -> connecting
[2024-06-18T12:26:42.124]  [NW] [WS] >> {"op":0,"d":{"server_id":"882554007653060649","user_id":"1151796939139076096","session_id":"8943bf4ab30521507fdb763f2125e279","token":"808d2c1cd68d5316"}}
[2024-06-18T12:26:42.125]  connecting -> connecting
[2024-06-18T12:26:42.126]  [NW] state change:
from {"code":0,"ws":true,"connectionOptions":{"endpoint":"tel-aviv10000.discord.media:443","serverId":"882554007653060649","token":"808d2c1cd68d5316","sessionId":"8943bf4ab30521507fdb763f2125e279","userId":"1151796939139076096"},"udp":false}
to {"code":1,"ws":true,"connectionOptions":{"endpoint":"tel-aviv10000.discord.media:443","serverId":"882554007653060649","token":"808d2c1cd68d5316","sessionId":"8943bf4ab30521507fdb763f2125e279","userId":"1151796939139076096"},"udp":false}
[2024-06-18T12:26:42.126]  [NW] [WS] << {"op":8,"d":{"v":4,"heartbeat_interval":13750.0}}
[2024-06-18T12:26:42.524]  [NW] [WS] << {"op":2,"d":{"streams":[{"type":"video","ssrc":328718,"rtx_ssrc":328719,"rid":"","quality":0,"active":false}],"ssrc":328717,"port":50008,"modes":["aead_aes256_gcm_rtpsize","aead_aes256_gcm","aead_xchacha20_poly1305_rtpsize","xsalsa20_poly1305_lite_rtpsize","xsalsa20_poly1305_lite","xsalsa20_poly1305_suffix","xsalsa20_poly1305"],"ip":"XXX","experiments":["fixed_keyframe_interval"]}}
[2024-06-18T12:26:42.525]  connecting -> connecting
[2024-06-18T12:26:42.525]  [NW] state change:
from {"code":1,"ws":true,"connectionOptions":{"endpoint":"tel-aviv10000.discord.media:443","serverId":"882554007653060649","token":"808d2c1cd68d5316","sessionId":"8943bf4ab30521507fdb763f2125e279","userId":"1151796939139076096"},"udp":false}
to {"code":2,"ws":true,"connectionOptions":{"endpoint":"tel-aviv10000.discord.media:443","serverId":"882554007653060649","token":"808d2c1cd68d5316","sessionId":"8943bf4ab30521507fdb763f2125e279","userId":"1151796939139076096"},"udp":true,"connectionData":{"ssrc":328717}}
[2024-06-18T12:26:42.527]  [NW] [WS] << {"op":11,"d":{"user_ids":["735021872047194114"]}}
[2024-06-18T12:26:42.527]  [NW] [WS] << {"op":18,"d":{"user_id":"735021872047194114","flags":2}}
[2024-06-18T12:26:42.528]  [NW] [WS] << {"op":20,"d":{"user_id":"735021872047194114","platform":0}}
[2024-06-18T12:26:55.067] Error stack: AbortError: The operation was aborted
    at abortListener (node:events:974:14)
    at AbortSignal.<anonymous> (node:events:1010:47)
    at AbortSignal.[nodejs.internal.kHybridDispatch] (node:internal/event_target:647:20)
    at AbortSignal.dispatchEvent (node:internal/event_target:589:26)
    at abortSignal (node:internal/abort_controller:283:10)
    at AbortController.abort (node:internal/abort_controller:314:5)
    at Timeout.<anonymous> (C:\Users\USER\OneDrive\Documents\Git\my-app\node_modules\@discordjs\voice\dist\index.js:2504:39)
    at listOnTimeout (node:internal/timers:559:17)
    at processTimers (node:internal/timers:502:7)

For successful entering we see the next log:

[2024-06-18T12:29:27.327] signalling -> connecting
[2024-06-18T12:29:28.994]  [NW] [WS] >> {"op":0,"d":{"server_id":"882554007653060649","user_id":"1151796939139076096","session_id":"fa61252752c15147fa90715f36449846","token":"c4a2573acd8c0d1e"}}
[2024-06-18T12:29:28.996] connecting -> connecting
[2024-06-18T12:29:28.997]  [NW] state change:
from {"code":0,"ws":true,"connectionOptions":{"endpoint":"tel-aviv10002.discord.media:443","serverId":"882554007653060649","token":"c4a2573acd8c0d1e","sessionId":"fa61252752c15147fa90715f36449846","userId":"1151796939139076096"},"udp":false}
to {"code":1,"ws":true,"connectionOptions":{"endpoint":"tel-aviv10002.discord.media:443","serverId":"882554007653060649","token":"c4a2573acd8c0d1e","sessionId":"fa61252752c15147fa90715f36449846","userId":"1151796939139076096"},"udp":false}
[2024-06-18T12:29:28.999]  [NW] [WS] << {"op":8,"d":{"v":4,"heartbeat_interval":13750.0}}
[2024-06-18T12:29:29.394]  [NW] [WS] << {"op":2,"d":{"streams":[{"type":"video","ssrc":308690,"rtx_ssrc":308691,"rid":"","quality":0,"active":false}],"ssrc":308689,"port":50005,"modes":["aead_aes256_gcm_rtpsize","aead_aes256_gcm","aead_xchacha20_poly1305_rtpsize","xsalsa20_poly1305_lite_rtpsize","xsalsa20_poly1305_lite","xsalsa20_poly1305_suffix","xsalsa20_poly1305"],"ip":"34.0.65.31","experiments":["fixed_keyframe_interval"]}}
[2024-06-18T12:29:29.395] connecting -> connecting
[2024-06-18T12:29:29.396]  [NW] state change:
from {"code":1,"ws":true,"connectionOptions":{"endpoint":"tel-aviv10002.discord.media:443","serverId":"882554007653060649","token":"c4a2573acd8c0d1e","sessionId":"fa61252752c15147fa90715f36449846","userId":"1151796939139076096"},"udp":false}
to {"code":2,"ws":true,"connectionOptions":{"endpoint":"tel-aviv10002.discord.media:443","serverId":"882554007653060649","token":"c4a2573acd8c0d1e","sessionId":"fa61252752c15147fa90715f36449846","userId":"1151796939139076096"},"udp":true,"connectionData":{"ssrc":308689}}
[2024-06-18T12:29:29.398]  [NW] [WS] << {"op":11,"d":{"user_ids":["735021872047194114"]}}
[2024-06-18T12:29:29.399]  [NW] [WS] << {"op":18,"d":{"user_id":"735021872047194114","flags":2}}
[2024-06-18T12:29:29.399]  [NW] [WS] << {"op":20,"d":{"user_id":"735021872047194114","platform":0}}
[2024-06-18T12:29:29.898]  [NW] [WS] >> {"op":1,"d":{"protocol":"udp","data":{"address":"<my_ip>","port":<my_port>,"mode":"xsalsa20_poly1305_lite"}}}
[2024-06-18T12:29:29.899] connecting -> connecting
[2024-06-18T12:29:29.900]  [NW] state change:
from {"code":2,"ws":true,"connectionOptions":{"endpoint":"tel-aviv10002.discord.media:443","serverId":"882554007653060649","token":"c4a2573acd8c0d1e","sessionId":"fa61252752c15147fa90715f36449846","userId":"1151796939139076096"},"udp":true,"connectionData":{"ssrc":308689}}
to {"code":3,"ws":true,"connectionOptions":{"endpoint":"tel-aviv10002.discord.media:443","serverId":"882554007653060649","token":"c4a2573acd8c0d1e","sessionId":"fa61252752c15147fa90715f36449846","userId":"1151796939139076096"},"udp":true,"connectionData":{"ssrc":308689}}
[2024-06-18T12:29:30.297]  [NW] [WS] << {"op":4,"d":{"video_codec":"H264","secure_frames_version":0,"secret_key":[...],"mode":"xsalsa20_poly1305_lite","media_session_id":"b1af6f38e4f57104a0476835aef0305e","audio_codec":"opus"}}        
[2024-06-18T12:29:30.298] connecting -> ready
[2024-06-18T12:29:30.299]  [NW] state change:
from {"code":3,"ws":true,"connectionOptions":{"endpoint":"tel-aviv10002.discord.media:443","serverId":"882554007653060649","token":"c4a2573acd8c0d1e","sessionId":"fa61252752c15147fa90715f36449846","userId":"1151796939139076096"},"udp":true,"connectionData":{"ssrc":308689}}
to {"code":4,"ws":true,"connectionOptions":{"endpoint":"tel-aviv10002.discord.media:443","serverId":"882554007653060649","token":"c4a2573acd8c0d1e","sessionId":"fa61252752c15147fa90715f36449846","userId":"1151796939139076096"},"udp":true,"connectionData":{"ssrc":308689,"encryptionMode":"xsalsa20_poly1305_lite","secretKey":{...},"sequence":30361,"timestamp":984628642,"nonce":0,"nonceBuffer":{"type":"Buffer","data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},"speaking":false,"packetsPlayed":0}}

After a few hours endpoint changed from tel-aviv to seattle and everything was working w/o timeouts already.

  1. Our bot server is located in US, could it be the problem, when traffic goes via European countries for example?
  2. Our bot can join voice channels on different guilds simultaneously.

Code sample

export async function join(channel: VoiceBasedChannel) {
    const connection = joinVoiceChannel({
        channelId: channel.id,
        guildId: channel.guild.id,
        selfDeaf: false,
        selfMute: true,
        adapterCreator: channel.guild.voiceAdapterCreator as unknown as DiscordGatewayAdapterCreator,
        debug: true
    });

    connection.setMaxListeners(100);

    connection.on('debug', (message) => {
        log.debug(Debug message for the ${channel.guild.id} guild: ${message});
    });

    connection.on('error', (error) => {
        log.error(Error for the ${channel.guild.id} guild: ${error});
    });

    log.debug(Connection to the ${channel.guild.id}[${channel.id}] voice channel was created successfully);

    try {
        await entersState(connection, VoiceConnectionStatus.Ready, 15e3);

        return true;
    } catch (error: any) {
        throw error;
    }
}

Versions

Issue priority

Medium (should be fixed soon)

Which partials do you have configured?

Not applicable

Which gateway intents are you subscribing to?

Guilds, GuildMembers, GuildVoiceStates, GuildPresences, GuildMessages, DirectMessages, MessageContent

I have tested this issue on a development release

No response

@balkrishan99
Copy link

  1. Increase Timeout for entersState:

    • Increase the timeout duration from 15 seconds to 30 seconds to accommodate potential network latency:
      await entersState(connection, VoiceConnectionStatus.Ready, 30e3);
  2. Connection Retry Mechanism*
    Add retry logic for failed connections:

    let retries = 3;
    while (retries > 0) {
        try {
            await entersState(connection, VoiceConnectionStatus.Ready, 30e3);
            break;
        } catch (error) {
            retries--;
            if (retries === 0) throw error;
        }
    }
  3. Optimize Geographical Routing:
    Use a hosting service closer to the expected Discord media servers (e.g., in Europe if guilds are located there).

  4. Debugging Logs:
    Add more detailed logging to isolate the cause of the issue:

    connection.on('stateChange', (oldState, newState) => {
        log.debug(`Connection state changed from ${oldState.status} to ${newState.status}`);
    });
  5. Update Packages:
    Ensure all related packages (discord.js, @discordjs/voice, etc.) are updated to the latest versions.

    npm update discord.js @discordjs/voice
  6. Evaluate Gateway Intents:
    Verify that the intents being subscribed to are necessary, as unnecessary intents can introduce additional latency or resource usage.

  7. Discord API Rate Limits:
    Ensure the bot adheres to Discord's rate limits for voice channel connections. Add delays between connections if necessary.

@SOLR4189
Copy link
Author

@balkrishan99 Thanks! Actually, I tried most of them, but in the end the problem was in other things:

  • Bot tried to join to a channel that was full already (channel has a user limit)
  • Bot tried to join to a channel when the bot was timeouted by user

Since my bot joins channels automatically and Discord doesn't provide an exact error on failure, I didn't know where the problem is.
After adding checks on these two cases the number of AbortError: The operation was aborted errors was decreased from thousands to less than 10 a week.

Could you only explain the number 3 in your list? Like I need to keep a several bot servers in different locations?

@balkrishan99
Copy link

Regarding point 3 in the list: "Optimize Geographical Routing," the suggestion refers to strategically placing your bot servers closer to the Discord voice servers most relevant to your user base. Here's an explanation:

Why Geographical Routing Matters
Discord's voice infrastructure uses media servers distributed globally. When your bot connects to a voice channel, Discord routes traffic through the media server closest to the guild's region. If your bot server is far from that media server (e.g., your bot is hosted in the US, but the media server is in Europe), the following issues may arise:

  1. Increased Latency: Data travels longer distances, introducing delays.
  2. Timeout Errors: Higher round-trip times (RTT) can cause connection timeouts, especially when the entersState timeout duration is too short.
  3. Packet Loss: Cross-region routing can increase the risk of packet drops.

How to Implement Geographical Routing

To address these challenges:

  1. Deploy Multiple Bot Servers:

    • Use hosting providers like AWS, Azure, or DigitalOcean to deploy servers in different geographical locations (e.g., US-East, US-West, Europe, Asia).
    • Select hosting locations based on where your guilds (or their regions) are most active.
  2. Determine Guild Voice Regions:

    • Use Discord's API to identify the voice region of the guild or voice channel.
    • Example:
      const region = guild.region; // Fetch the guild's region
      log.debug(`Guild ${guild.id} is in region: ${region}`);
  3. Route Bot Requests to the Closest Server:

    • Use DNS-based routing tools like AWS Route 53 or Cloudflare to automatically direct requests from users to the nearest bot server.
    • Alternatively, manage connections programmatically:
      • Have a central control server that forwards connection requests to the most suitable bot server based on the guild's region.
  4. Test and Monitor:

    • Log connection attempts and successes to ensure that latency and timeout errors reduce after implementing this strategy.
    • Example:
      log.debug(`Routing bot to server closest to ${region}`);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants