C# Discord Bot Encountering StackOverflowException

I’m developing a Discord bot using Discord.NET version 0.9, and I’m facing a stack overflow exception whenever I run it. This issue arises when I instantiate my main program class within my functions class. I’m concerned that this may be tied to how I’m managing static instances, but I’m at a loss for the cause of the circular reference. My bot compiles successfully, yet it crashes during execution. Below is the code I’m working with:

MainBot.cs:

using System;
using System.Linq;
using Discord;
using Discord.Commands;
using System.IO;

namespace MyDiscordBot
{
    class MainBot
    {
        CommandHandler commandHandler = new CommandHandler();
        public static MainBot _instance = new MainBot();

        static void Main(string[] args)
        {
            new MainBot().Initiate();
        }

        public DiscordClient _discordClient;

        public void Initiate()
        {
            Console.Title = "My Discord Bot";

            _discordClient = new DiscordClient(config =>
            {
                config.AppName = "MyBot";
                config.LogLevel = LogSeverity.Info;
                config.LogHandler = LogMessage;
            });

            _discordClient.UsingCommands(commands =>
            {
                commands.PrefixChar = '!';
                commands.AllowMentionPrefix = true;
                commands.HelpMode = HelpMode.Public;
            });

            var botToken = "YOUR_TOKEN_HERE";
            commandHandler.ConfigureCommands(_discordClient);
            setupEventHandlers();

            _discordClient.ExecuteAndWait(async () =>
            {
                await _discordClient.Connect(botToken, TokenType.Bot);
                SetGameStatus(_discordClient, "!help for commands");
            });
        }

        public void SetGameStatus(DiscordClient client, string status)
        {
            client.SetGame(status);
        }

        public void setupEventHandlers()
        {
            _discordClient.UserJoined += async (sender, eventArgs) =>
            {
                await eventArgs.Server.GetChannel(123456789).SendMessage($"Welcome {eventArgs.User.Mention} to our server!");
            };
        }

        public void LogMessage(Object sender, LogMessageEventArgs args)
        {
            Console.WriteLine($"[{args.Severity}] [{args.Source}] {args.Message}");
        }
    }
}

CommandHandler.cs:

using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;

namespace MyDiscordBot
{
    class CommandHandler
    {
        public static CommandHandler _instance = new CommandHandler();
        UtilityMethods utilityMethods = new UtilityMethods();

        public void ConfigureCommands(DiscordClient client)
        {
            var commandService = client.GetService<CommandService>();

            commandService.CreateCommand("echo")
                .Description("Echoes a message")
                .Parameter("text", ParameterType.Unparsed)
                .Do(async (e) =>
                {
                    await e.Channel.SendMessage($"Echo: {e.GetArg("text")}");
                });

            commandService.CreateCommand("refresh")
                .Description("Refreshes user data")
                .Do(async (e) =>
                {
                    if (e.User.ServerPermissions.Administrator)
                    {
                        foreach (User member in e.Server.Users)
                        {
                            if (!member.IsBot)
                                utilityMethods.RefreshUserData(member);
                        }
                        await e.Channel.SendMessage("Data refresh completed!");
                    }
                    else
                    {
                        await e.Channel.SendMessage("You don't have permission!");
                    }
                });
        }
    }
}

UtilityMethods.cs:

using Discord;
using System;
using System.IO;

namespace MyDiscordBot
{
    class UtilityMethods
    {
        MainBot mainBot = new MainBot();

        public void RefreshUserData(User user)
        {
            string userFilePath = @"C:\BotData\Users\" + user.Id + ".txt";
            
            if (File.Exists(userFilePath))
            {
                File.Delete(userFilePath);
            }
            
            string[] userData = { user.Name, GetUserRoles(user) };
            File.WriteAllLines(userFilePath, userData);
        }

        public string GetUserRoles(User user)
        {
            string roles = "";
            foreach (Role userRole in user.Roles)
            {
                if (userRole.Name != "Admin" && userRole.Name != "@everyone")
                {
                    roles += userRole.Name + "|";
                }
            }
            return roles;
        }

        public string GetStoredUsername(string userId)
        {
            string userFilePath = @"C:\BotData\Users\" + userId + ".txt";
            if (File.Exists(userFilePath))
            {
                string[] data = File.ReadAllLines(userFilePath);
                return data[0];
            }
            return null;
        }
    }
}

The issue occurs when the UtilityMethods class attempts to create a new instance of MainBot. Can anyone suggest what might be triggering this circular reference problem?

Your problem’s in the UtilityMethods class - you’re creating a new MainBot instance there. I’ve hit this same issue before. Here’s what’s happening: MainBot constructor starts CommandHandler, CommandHandler creates UtilityMethods, then UtilityMethods tries to make another MainBot. Boom - infinite recursion. Don’t create a new MainBot in UtilityMethods. Instead, pass the DiscordClient directly to your utility methods or use the existing static _instance. You could use MainBot._instance._discordClient but it’s pretty messy. Better approach: just pass the client as a parameter to RefreshUserData.

You’ve got a circular reference in your object creation. MainBot creates CommandHandler, which creates UtilityMethods, which tries to create another MainBot. That’s an infinite loop that’ll cause a stack overflow. I’ve hit this same issue in my bot projects. Don’t create new MainBot instances in your utility classes. Pass the existing MainBot instance as a parameter instead, or restructure so you don’t need that access. Since you’re using static instances, you could use MainBot._instance if you need to, but dependency injection would be cleaner.

yeah, it’s the circular dependency causing this mess. your utilityMethods shouldn’t create new MainBot instances - that’s what’s breaking everything. just remove that line and pass the data you need directly to the method. the utility class doesn’t even need to know about MainBot.