Adonis custom command error

I’m trying to create a new “trelloban” command (through a “plugin”-esque feature of Adonis) and have it both send an embed to a channel in Discord and add a card to a Trello.

When I run the “cmds” command in-game, it says: “334: Attempt to concatenate field ‘Description’ (a table value)”.

After attempting (and failing) to find other info about this issue, I’m stuck. I know it has to be the plugin ModuleScript, because when I remove that, I can run “cmds” and it works fine.

@Davey_Bones / @Sceleratis - perhaps you have some insight into why this isn’t working?

Here’s the code from the plugin (the “standardTargetUrl” variable has been removed on purpose, and does not seem to be causing an issue):

server = nil -- Mutes warnings about unknown globals
service = nil
Functions = nil
Core = nil
Admin = nil
TrelloAPI = require(game.ServerScriptService.TrelloAPI)
board = TrelloAPI:GetBoardID("Adonis Board")
permlist = TrelloAPI:GetListID("Permanent Bans",board)
templist = TrelloAPI:GetListID("Temporary Bans",board)
servlist = TrelloAPI:GetListID("Server Bans",board)
local http = game:GetService("HttpService")

-- Converts time to ISO8601
local function getCurrentTime()
	local currentTime = os.date("!*t")

	local hour = currentTime.hour
	local minute = currentTime.min
	local second = currentTime.sec

	local day = currentTime.day
	local month = currentTime.month
	local year = currentTime.year

	-- ISO8601 support
	if hour < 10 then
		hour = 0 .. hour
	end
	if minute < 10 then
		minute = 0 .. minute
	end
	if second < 10 then
		second = 0 .. second
	end
	if day < 10 then
		day = 0 .. day
	end
	if month < 10 then
		month = 0 .. month
	end
	if year < 10 then
		year = 0 .. year
	end

	return ("%s-%s-%sT%s:%s:%sZ"):format(year, month, day, hour, minute, second)
end

local function banEmbed(mod,bannedPlayer,length,reason) --length is a string
	local function NewJSON(optionstbl,content)
		local obj = {
			['embeds'] = {optionstbl},
			['content'] = content,
			['avatar_url'] = "https://cdn.discordapp.com/avatars/249399670638379008/3755914fea3f6ec0ebdc4c7048d7b636.png"
		}
	
		return http:JSONEncode(obj)
	end
	local function embed(username,userId)
		local options = {}
		options.title = "Player Banned"
		options.description = "["..username.."](https://www.roblox.com/users/"..bannedPlayer.UserId.."/profile)".." has been banned."
		options.color = 15066830
		options.timestamp = getCurrentTime()
		options.footer = {
		    icon_url = "",
		    text = "Ban Logs"
		}
		options.fields = {
	      {
	        name = "Moderator",
	        value = mod,
	        inline = true
	      },
	      {
	        name = "Length",
			value = length,
	        inline = true
	      },
	      {
	        name = "Reason",
	        value = reason
	      }
		}
		return options
	end
	local tableCreate = embed(bannedPlayer.Name,bannedPlayer.UserId)
	local msgObj = NewJSON(tableCreate,bannedPlayer.Name.." banned by "..mod.Name..".")
	http:PostAsync(standardTargetUrl, msgObj)
end


return function()
	server.Commands.TrelloBan = {
		Prefix = server.Settings.Prefix;	-- Prefix to use for command
		Commands = {"trelloban"};	-- Commands
		Args = {"player","length", "reason"};	-- Command arguments
		Description = {"The second argument (length) is in days. The third argument (reason) can be 'server', 'perm', or a length in days from 1 to 14."};	-- Command Description
		Hidden = false; -- Is it hidden from the command list?
		Fun = false;	-- Is it fun?
		AdminLevel = "Moderators";	    -- Admin level; If using settings.CustomRanks set this to the custom rank name (eg. "Baristas")
		Function = function(plr,args)    -- Function to run for command
			player = args[1]
			length = args[2]
			reason = args[3]
			if tostring(length) == "perm" then
				local level = Admin.GetLevel(plr)
				if level >= 3 then
					Admin.AddBan(player, true)
					Functions.Hint("Permanently banned "..tostring(player.Name),{plr})
					if not TrelloAPI:GetCardID(player.Name..":"..player.UserId,board) then
						local desc = tostring("Banned by "..plr.Name.." for "..reason.." permanently.")
						TrelloAPI:AddCard(player.Name..":"..player.UserId,desc,permlist)
					end
					banEmbed(plr,player,"Permanent",reason)
				end
			elseif tostring(length) == "server" then
				local level = Admin.GetLevel(plr)
				for i,v in next,service.GetPlayers(plr,args[1],false,false,true) do
					if level > Admin.GetLevel(v) then 
						Admin.AddBan(v)
						Functions.Hint("Server banned "..tostring(v.Name),{plr})
						local desc = tostring("Server banned by "..plr.Name.." for "..reason..".")
						TrelloAPI:AddCard(player.Name..":"..player.UserId,desc,servlist)
						banEmbed(plr,player,"Server",reason)
					end
				end
			elseif tonumber(length) < 14 then
				local level = Admin.GetLevel(plr)
				for i,v in next,service.GetPlayers(plr,args[1],false,false,true) do
					if level > Admin.GetLevel(v) then 
						local time = ((tonumber(length)*60)*60)*24
						assert(args[1] and args[2], "Argument missing or nil")
						for i,v in next,service.GetPlayers(plr, args[1], false, false, true) do
							endTime = tonumber(os.time())+tonumber(time)
							local timebans = Core.Variables.TimeBans
							local data = {
								Name = v.Name;
								UserId = v.UserId;
								EndTime = endTime;
							}
							
							table.insert(timebans, data)
							Core.DoSave({
								Type = "TableAdd";
								Table = "TimeBans";
								Parent = "Variables";
								Value = data;
							})
							
							v:Kick("Banned until "..endTime)
							Functions.Hint("Banned "..v.Name.." for "..time,{plr})
						end
						local desc = tostring("Banned by "..plr.Name.." for "..reason.." for "..tonumber(length).." days until "..endTime..".")
						TrelloAPI:AddCard(player.Name..":"..player.UserId,desc,templist)
						banEmbed(plr,player,length.." days",reason)
					end
				end
				else
				Functions.Hint("Your ban length was invalid. Please try again with 'perm', 'server', or a length (in days) between 1 and 14.",{plr})
			end
		end
	}
end

Since your plugin doesn’t have 334 lines, I’m assuming this is an issue with how your plugin is telling Adonis what your command does. Take a look at the example plugin for Adonis. Your Description Variable needs to be defined as shown in that example in order for the cmds command to explain to the user what the command does.

You’re currently defining Description as {"The second argument (length) is in days. The third argument (reason) can be 'server', 'perm', or a length in days from 1 to 14."};. Description should be defined as: "The second argument (length) is in days. The third argument (reason) can be 'server', ‘perm’, or a length in days from 1 to 14.

By placing Description in a table defined by {…}, Adonis is throwing an error when it can’t find the string of your command description.
You may have confused this with the other tables that are required for the other fields. The command variable is all commands that can be used. The Args is a table that tells Adonis how many args and what each one is. However, the description is a string. Not a table.

:man_facepalming: I completely overlooked that…
Thank you so much. I’ll test it now and see if it works.

Edit: Many facepalms later… it solved the issue! Thanks again - I’m going to follow up about a separate issue in DMs.