title: Discord.gateway warning "Shard ID None heartbeat blocked for more than 10 seconds." while using pandas
body: <p>So I've made a discord bot using discord.py in python and have been running it for some time. However, recently the bot has randomly started to die. So I added the logging library to my program to try and find out what was happening and I got this log this morning:</p>
  <p><a href="https://pastebin.com/s5yjQMs7" rel="nofollow noreferrer">https://pastebin.com/s5yjQMs7</a></p>
  <p>This error traceback goes on forever referencing multiple pandas files. My discord bot code:</p>
  <pre><code># Import libraries
  import asyncio
  import random
  import AO3
  import pandas as pd
  from discord.ext import commands
  import logging
  
  
  # Function to setup the dataframe
  def dataframeSetup():
      # Create the dataframe
      df = pd.read_csv(
          &quot;https://docs.google.com/spreadsheets/d/16QtBJEtvV5a5DheR78x5AsoVA5b2DpXD1mq-x3lCFiA/export?format=csv&quot;,
          names=[&quot;NaN&quot;, &quot;Title&quot;, &quot;Author&quot;, &quot;Ship(s)&quot;, &quot;Type&quot;, &quot;Series&quot;, &quot;Status&quot;, &quot;Smut&quot;, &quot;No of words&quot;, &quot;No of chapters&quot;,
                 &quot;Link&quot;])
      # Remove first two lines
      df = df.iloc[2:]
      # Remove the first column
      df.drop(&quot;NaN&quot;, axis=1, inplace=True)
      # Create variable to store the index of the first empty row
      firstEmptyRow = 0
      # Iterate over every row
      for index, row in df.iterrows():
          # Test if every cell is empty
          if row.isnull().all():
              # Set firstEmptyRow to the index (it is minus 2 because the index of the dataframe starts at 2)
              firstEmptyRow = index - 2
              break
      # Return the final dataframe
      return df.iloc[0:firstEmptyRow]
  
  # Function to make random quotes
  def quoteMaker(df):
      # Grab a random fic
      randomFic = df.iloc[random.randint(2, len(df))]
      # Create AO3 session
      ao3Session = AO3.Session(&quot;username&quot;, &quot;password&quot;)
      # Create work object
      work = AO3.Work(AO3.utils.workid_from_url(randomFic[&quot;Link&quot;]), ao3Session)
      # Get chapter amount
      chapterAmount = work.chapters
      # Get chapter text for a random chapter
      randomChapter = random.randint(1, chapterAmount)
      randomChapterText = work.get_chapter_text(randomChapter)
      # Convert the chapter text into a list
      textList = list(filter(None, randomChapterText.split(&quot;\n&quot;)))
      # Return random string
      return textList[random.randint(0, len(textList) - 1)], work, randomChapter, ao3Session
  
  # Function to create trivia
  def triviaMaker(triviaDone):
      # Test if all trivia questions have been done
      if len(triviaDone) == len(df1):
          # They've all been done, so clear the list and start again
          triviaDone.clear()
      # Generate a random index and use that to get a random trivia question
      randomIndex = random.randint(0, len(df1)) - 1
      randomTrivia = df1.iloc[randomIndex]
      # Test if the selected trivia question has been done before
      while randomIndex in triviaDone:
          # Trivia has already been done recently so try another one
          randomTrivia = df.iloc[random.randint(0, len(df1))]
      # Add the selected trivia question's index to the list
      triviaDone.append(randomIndex)
      # Return the formatted string as well as the correct index to allow for validation
      return f'''{randomTrivia[&quot;Question&quot;]}:
  1. {randomTrivia[&quot;Option 1&quot;]}
  2. {randomTrivia[&quot;Option 2&quot;]}
  3. {randomTrivia[&quot;Option 3&quot;]}
  4. {randomTrivia[&quot;Option 4&quot;]}''', randomTrivia, randomTrivia[&quot;Correct Option&quot;]
  
  
  def record(work):
      # Create initial array to store results
      ficResults = []
      # Open file and write existing results to ficResults
      with open(&quot;QuoteResults.txt&quot;, &quot;r&quot;) as file:
          for line in file.readlines():
              ficResults.append(line)
      # Test if fic already exists in the results
      found = False
      for count, fic in enumerate(ficResults):
          if str(work.workid) in fic:
              # Fic already exists
              found = True
              break
      # Assign the new result
      if found == True:
          # Increment the result
          ficResults[count] = f&quot;22561831, {int(ficResults[count][-2:]) + 1}\n&quot;
      else:
          # Create new result
          ficResults.append(f&quot;{work.workid}, 1\n&quot;)
      # Write to file
      with open(&quot;QuoteResults.txt&quot;, &quot;w&quot;) as file:
          for result in ficResults:
              file.write(result)
  
  
  def authorGrab(work, session):
      # Function to grab only the authors
      return session.request(work.url).findAll(&quot;h3&quot;, {&quot;class&quot;: &quot;byline heading&quot;})[0].text.replace(&quot;\n&quot;, &quot;&quot;)
  
  
  # Initialise discord variables
  token = &quot;discord token&quot;
  client = commands.Bot(command_prefix=&quot;!&quot;, case_insensitive=True)
  # Initialise the dataframe
  df = dataframeSetup()
  # Initialise trivia variables
  df1 = pd.read_csv(&quot;Trivia.txt&quot;, delimiter=&quot;/&quot;,
                    names=[&quot;Question&quot;, &quot;Option 1&quot;, &quot;Option 2&quot;, &quot;Option 3&quot;, &quot;Option 4&quot;, &quot;Correct Option&quot;])
  # Initialise asked trivia questions list
  triviaDone = []
  # Initialise channel ID variables using a file
  with open(&quot;IDs.txt&quot;, &quot;r&quot;) as file:
      channelIDs = file.read().splitlines()
  
  # Initialise logging
  logger = logging.getLogger(&quot;discord&quot;)
  logger.setLevel(logging.DEBUG)
  handler = logging.FileHandler(filename=&quot;quoteBot.log&quot;, encoding=&quot;utf-8&quot;, mode=&quot;a&quot;)
  handler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s:%(name)s: %(message)s'))
  logger.addHandler(handler)
  
  # Register !quote command
  @client.command()
  @commands.cooldown(1, 10, commands.BucketType.default)
  async def quote(ctx):
      if ctx.channel.id == int(channelIDs[0][10:]):
          quote = &quot;&quot;
          # Test whether the quote is longer than 10 words
          while len(quote.split()) &lt; 10:
              # Grab quote and related attributes
              quote, work, randomChapter, session = quoteMaker(df)
          # Grab authors
          authors = authorGrab(work, session)
          # Print quote and attributes
          await ctx.channel.send(quote)
          await ctx.channel.send(f&quot;-{work.title} chapter {randomChapter} by {authors}. Link {work.url}&quot;)
          record(work)
  
  
  # Register !trivia command
  # This command can only be used once every 60 seconds server-wide
  @client.command()
  @commands.cooldown(1, 60, commands.BucketType.default)
  async def trivia(ctx):
      shortenedIDString = channelIDs[1][11:]
      for id in shortenedIDString.split(&quot;, &quot;):
          if ctx.channel.id == int(id):
              # Display trivia question
              triviaString, randomTrivia, correctIndex = triviaMaker(triviaDone)
              await ctx.channel.send(triviaString)
  
              # Function to check if an answer is correct
              def check(message):
                  # Check if answer is correct
                  if &quot;!answer&quot; in message.content:
                      return message.content == f&quot;!answer {randomTrivia.iloc[int(correctIndex)]}&quot; or message.content == f&quot;!answer {int(correctIndex)}&quot;
  
              # Try and except statement to catch timeout error
              try:
                  # Wait for user response
                  await client.wait_for(&quot;message&quot;, check=check, timeout=15)
                  # User response is correct
                  await ctx.channel.send(&quot;Correct answer&quot;)
              except asyncio.TimeoutError:
                  # Time has run out
                  await ctx.channel.send(&quot;Times up, better luck next time&quot;)
  
  
  # Register empty !answer command
  # This is only needed to stop an error being returned
  @client.command()
  async def answer(ctx):
      return None
  
  
  # Register !cruzie command
  @client.command()
  @commands.cooldown(1, 5, commands.BucketType.default)
  async def cruzie(ctx):
      # User has types !cruzie so do secret
      await ctx.channel.send(&quot;https://giphy.com/gifs/midland-l4FsJgbbeKQC8MGBy&quot;)
  
  
  # Register !murica command
  @client.command()
  @commands.cooldown(1, 5, commands.BucketType.default)
  async def murica(ctx):
      # User has typed !murica so play murica gif
      await ctx.channel.send(&quot;https://tenor.com/view/merica-gif-9091003&quot;)
  
  
  # Register !gamer command
  @client.command()
  @commands.cooldown(1, 5, commands.BucketType.default)
  async def gamer(ctx):
      # User has typed !gamer so play gamers gif
      await ctx.channel.send(&quot;https://tenor.com/view/hello-gamers-hello-hi-howdy-whats-up-gif-12988393&quot;)
  
  
  # Register !stinky command
  @client.command()
  @commands.cooldown(1, 5, commands.BucketType.default)
  async def stinky(ctx):
      # User has typed !stinky so play srinky gif
      await ctx.channel.send(&quot;https://tenor.com/view/monke-uh-oh-stinky-uh-oh-stinky-monke-gif-18263597&quot;)
  
  
  # Run when discord bot has started
  @client.event
  async def on_ready():
      # Get channel ID for test channel
      channel = client.get_channel(&quot;debug channel&quot;)
      # Send message to user signalling that the bot is ready
      await channel.send(&quot;Running&quot;)
  
  
  # Catch discord errors
  @client.event
  async def on_command_error(ctx, error):
      if isinstance(error, commands.CommandOnCooldown):
          # CommandOnCooldown error detected
          await ctx.channel.send(f&quot;Command is on cooldown, try again in {round(error.retry_after, 2)} seconds&quot;)
  
  
  # Start discord bot
  client.run(token)
  </code></pre>
  <p>If anyone can figure out why this error occurs, that'll be greatly appreciated.</p>