C# Discord.NET 1.0 - Issues with command modules not working properly

Hey everyone! I’m working on a Discord bot using Discord.NET 1.0 and C# but running into some troubles.

I want to organize my bot commands into separate classes instead of putting everything in one big command file. This should make the code cleaner and easier to manage.

I have my main bot setup working fine, along with the command handler and some basic commands. But when I try to create a new command module, it keeps showing “Unknown command” errors.

Here’s my bot startup code:

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

namespace BotApp
{
    public class BotProgram
    {
        public static void Main(string[] args) =>
            new BotProgram().StartBot().GetAwaiter().GetResult();

        private DiscordSocketClient botClient;
        private CommandProcessor processor;

        public async Task StartBot()
        {
            botClient = new DiscordSocketClient();
            var botToken = "MyTokenHere";

            await botClient.LoginAsync(TokenType.Bot, botToken);
            await botClient.StartAsync();

            var dependencies = new DependencyMap();
            dependencies.Add(botClient);

            processor = new CommandProcessor();
            await processor.Setup(dependencies);

            await Task.Delay(-1);
        }
    }
}

My command processor:

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

namespace BotApp
{
    public class CommandProcessor
    {
        private CommandService cmdService;
        private DiscordSocketClient botClient;
        private IDependencyMap dependencies;

        public async Task Setup(IDependencyMap deps)
        {
            botClient = deps.Get<DiscordSocketClient>();
            cmdService = new CommandService();
            deps.Add(cmdService);
            dependencies = deps;

            await cmdService.AddModulesAsync(Assembly.GetEntryAssembly());
            botClient.MessageReceived += ProcessMessage;
        }

        public async Task ProcessMessage(SocketMessage msg)
        {
            var userMessage = msg as SocketUserMessage;
            if (userMessage == null) return;

            int commandStart = 0;
            if (!(userMessage.HasMentionPrefix(botClient.CurrentUser, ref commandStart) || 
                  userMessage.HasCharPrefix('>', ref commandStart))) return;

            var cmdContext = new CommandContext(botClient, userMessage);
            var executeResult = await cmdService.ExecuteAsync(cmdContext, commandStart, dependencies);

            if (!executeResult.IsSuccess)
                await userMessage.Channel.SendMessageAsync($"Error: {executeResult.ErrorReason}");
        }
    }
}

Working commands module:

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

namespace BotApp.Commands.General
{
    public class GeneralCommands : ModuleBase
    {
        [Command("info")]
        [Summary("Shows bot information")]
        public async Task ShowInfo()
        {
            await ReplyAsync("This is my Discord bot!");
        }

        [Command("ping")]
        [Summary("Checks bot response time")]
        public async Task CheckPing()
        {
            await ReplyAsync("Pong!");
        }
    }
}

Broken command module (keeps saying unknown command):

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

namespace BotApp.Commands.General
{
    class TextCommands : ModuleBase
    {
        [Command("repeat")]
        [Alias("copy")]
        [Summary("Repeats the given text")]
        public async Task RepeatText([Remainder] string text)
        {
            await ReplyAsync(text);
        }
    }
}

The repeat command just won’t work no matter what I try. What am I missing here? Any help would be great!

Hit this exact issue last week! Your TextCommands class is missing the public modifier - that’s the problem. Discord.NET uses reflection to find command modules, but it can’t see classes that aren’t public. You wrote class TextCommands instead of public class TextCommands, so the assembly scanner just skips it when AddModulesAsync runs. Classic C# gotcha since the compiler won’t warn you about visibility problems. Just add public before class and your repeat command will work right away.

Yeah, classic beginner mistake - make your TextCommands class public. You’ve got class TextCommands but need public class TextCommands. Discord.net can’t see internal classes when scanning for modules, so yours never gets registered. We’ve all been there!

Your TextCommands class probably isn’t public. When you don’t specify public/private, classes default to internal visibility - so the command service can’t find and register the module during AddModulesAsync. Your working GeneralCommands is declared as public class, but the broken one’s just class TextCommands. Change it to public class TextCommands : ModuleBase and you’re good. I hit this exact bug when I started with Discord.NET and wasted way too much time on such a simple fix.