Attempt to index nil issue

Hello there,

I’m making a shop with skins and traps but something doesn’t seems to be working

Error message:

Players.The_UncleDev.PlayerGui.MenuGui.Shop.LocalShopScript:243: attempt to index nil with 'Info'  -  Client - LocalShopScript:243
  11:56:12.398  Stack Begin  -  Studio
  11:56:12.399  Script 'Players.The_UncleDev.PlayerGui.MenuGui.Shop.LocalShopScript', Line 243 - function checkStatus  -  Studio - LocalShopScript:243
  11:56:12.401  Script 'Players.The_UncleDev.PlayerGui.MenuGui.Shop.LocalShopScript', Line 395 - function previewItem  -  Studio - LocalShopScript:395
  11:56:12.402  Script 'Players.The_UncleDev.PlayerGui.MenuGui.Shop.LocalShopScript', Line 77 - function showScreen  -  Studio - LocalShopScript:77
  11:56:12.403  Script 'Players.The_UncleDev.PlayerGui.MenuGui.Shop.LocalShopScript', Line 98  -  Studio - LocalShopScript:98
  11:56:12.403  Stack End  -  Studio

LocalShopScript:

-- services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TweenService = game:GetService("TweenService")
local MarketService = game:GetService("MarketplaceService")
-- folders
local Functions = ReplicatedStorage:WaitForChild("Functions")
local Events = ReplicatedStorage:WaitForChild("Events")
local skinFolder = ReplicatedStorage:WaitForChild("Skins")
local trapFolder = ReplicatedStorage:WaitForChild("Traps")
-- remotes
local getData = Functions:WaitForChild("getData")
local selectItemFunc = Functions:WaitForChild("selectItem")
local previewItemFunc = Functions:WaitForChild("previewItem")
local buyItem = Functions:WaitForChild("buyItem")
local buyCurrency = Functions:WaitForChild("buyCurrency")
local togglePopup = Events:WaitForChild("togglePopup")
-- references
local player = game.Players.LocalPlayer
local shop = script.Parent
local popup = shop.Parent.Popup
local shopPreview = shop.Preview
local shopMenu = shop.ShopMenu
local topMenu = shopMenu.TopMenu

local selectBtn = shop.Select
local tickets = shop.Tickets
local backBtn = shop.Back

local homeBtn = topMenu.Home
local skinBtn = topMenu.Skin
local trapBtn = topMenu.Traps

local homeScreen = shopMenu.Home
local skinScreen = shopMenu.Skin
local trapScreen = shopMenu.Traps
local activeScreen

-- modules
local availableCharacters = require(skinFolder:WaitForChild("List"))
local availableTraps = require(trapFolder:WaitForChild("List"))

local ownedSkins = {}
local killerSelection
local ownedTraps = {}
local trapSelection

-- colors :D
local colors = {
	red = Color3.fromRGB(170, 0, 0),
	green = Color3.fromRGB(100, 193, 66),
	yellow = Color3.fromRGB(255, 216, 0),
	neutral = Color3.fromRGB(225, 168, 139),
	white = Color3.fromRGB(255, 255, 255),
	black = Color3.fromRGB(0, 0, 0)
}

local function showScreen(screen)
	homeScreen.Visible = false
	skinScreen.Visible = false
	trapScreen.Visible = false
	selectBtn.Visible = false
	for index, child in pairs(shopPreview:GetChildren()) do
		if not child:IsA("UICorner") then
			child:Destroy()
		end
	end
	
	screen.Visible = true
	activeScreen = screen
	
	if screen.Name == "Skin" then
		shopPreview.Visible = true
		shopMenu.UIStroke.Color = Color3.fromRGB(255, 50, 0)
		shop.Title.Text = "Shop:Skin section"
		if killerSelection then
			previewItem(screen:FindFirstChild(killerSelection), "Skin")
		else
			previewItem(screen:FindFirstChild("Clare"), "Skin")
		end
	elseif screen.Name == "Traps" then
		shopPreview.Visible = true
		shopMenu.UIStroke.Color = Color3.fromRGB(255,0,0)
		shop.Title.Text = "Shop:Trap section"
		if trapSelection then
			previewItem(screen:FindFirstChild(trapSelection), "Traps")
		else
			previewItem(screen:FindFirstChild("Clare"), "Traps")
		end
	else
		shopPreview.Visible = false
		shopMenu.UIStroke.Color = Color3.fromRGB(2, 130, 186)
		shop.Title.Text = "Shop:Home page"
	end
end

homeBtn.Activated:Connect(function()
	showScreen(homeScreen)
end)

skinBtn.Activated:Connect(function()
	showScreen(skinScreen)
end)

trapBtn.Activated:Connect(function()
	showScreen(trapScreen)
end)

-- control popup ui
local function popupPrompt(msg, cancelOption, location)
	local result
	local cancel
	local confirm
	
	popup.Visible = true
	popup.Title.Text = msg
	popup.Buttons.Cancel.Visible = false
	
	if cancelOption then
		popup.Buttons.Cancel.Visible = true
		cancel = popup.Buttons.Cancel.Activated:Connect(function()
			if cancel then
				cancel:Disconnect()
			end
			if confirm then
				confirm:Disconnect()
			end
			popup.Visible = false
			result = false
		end)
	end
	
	confirm = popup.Buttons.Okay.Activated:Connect(function()
		if cancel then
			cancel:Disconnect()
		end
		if confirm then
			confirm:Disconnect()
		end
		popup.Visible = false
		result = true
	end)
	
	while not result do
		wait()
	end
	
	return result
end
togglePopup.OnClientEvent:Connect(popupPrompt)

-- changes how the shop icons will look
local function setStatus(btn, status)
	selectBtn.Info.Visible = false
	
	-- default
	if status == "For Sale" then
		btn.Info.Tickets.Visible = true
		btn.Info.Status.TextColor3 = colors["yellow"]
		btn.Title.TextStrokeColor3 = colors["yellow"]
		btn.ViewportFrame.BackgroundColor3 = colors["neutral"]
	else -- owned or selected
		btn.Info.Status.Text = status
		btn.Info.Tickets.Image = "rbxassetid://54653911"
	end
	
	if status == "Owned" then
		btn.Info.Status.TextColor3 = colors["green"]
		btn.Title.TextStrokeColor3 = colors["green"]
		btn.ViewportFrame.BackgroundColor3 = colors["green"]
	elseif status == "Selected" then
		btn.Info.Status.TextColor3 = colors["red"]
		btn.Title.TextStrokeColor3 = colors["red"]
		btn.ViewportFrame.BackgroundColor3 = colors["red"]
	elseif status == "Off-Sale" then
		btn.Info.Status.TextColor3 = colors["neutral"]
		btn.Title.TextStrokeColor3 = colors["neutral"]
		btn.ViewportFrame.BackgroundColor3 = colors["black"]
		btn.ViewportFrame.ImageColor3 = colors["black"]
	end
end

-- checks our local table of the players session data, reduces unnecessary server calls
local function playerOwnsItem(itemType, name)
	if ownedSkins and ownedTraps then
		local dictionary
		if itemType == "Skin" then
			dictionary = ownedSkins
		elseif itemType == "Traps"  then
			dictionary = ownedTraps
		end
		if dictionary then
			for index, item in pairs(dictionary) do
				if item == name then
					return true
				end
			end
		end
	end
	return false
end

local function loadData()
	-- gets the latest player data
	local data = getData:InvokeServer()
	if data then
		ownedSkins = data.skins
		killerSelection = data.killer
		ownedTraps = data.traps
		trapSelection = data.trap
	end
end

-- updates store whenever anything changes
local function updateStore()
	loadData()
	-- updates the players money tally
	tickets.Amount.Text = player.Tickets.Value
	
	-- sets skin icons to appropriate status
	for index, gui in pairs(skinScreen:GetChildren()) do
		if gui:IsA("TextButton") and gui.Visible == true then
			if gui.Name == killerSelection then
				setStatus(gui, "Selected")
			elseif playerOwnsItem("Clare", gui.Name) then
				setStatus(gui, "Owned")
			elseif availableCharacters[gui.Name]["For Sale"] == false then
				setStatus(gui, "Off-Sale")
			end
		end
	end
	
	-- setup trap icons
	for index, gui in pairs(trapScreen:GetChildren()) do
		if gui:IsA("TextButton") and gui.Visible == true then
			if gui.Name == trapSelection then
				setStatus(gui, "Selected")
			elseif playerOwnsItem("NormalTrap", gui.Name) then
				setStatus(gui, "Owned")
			end
		end
	end
	
	
	-- redraw the viewport model
	if shopPreview:FindFirstChild("WorldModel") and shopPreview.WorldModel:FindFirstChildOfClass("Model") then
		local model = shopPreview.WorldModel:FindFirstChildOfClass("Model")
		if model then
			displayModel(model:Clone(), model.ItemType.Value)
		end
	end
end
player.Tickets.Changed:Connect(updateStore)

local function checkStatus(btn)
	if btn.Info.Status.TextColor3 == colors["red"] then
		return "Selected"
	elseif btn.Info.Status.TextColor3 == colors["green"] then
		return "Owned"
	elseif btn.Info.Status.TextColor3 == colors["yellow"] then
		return "For Sale"
	elseif btn.Info.Status.TextColor3 == colors["neutral"] then
		return "Off-Sale"
	end
end

local function setupPreview(status)
	shop.Select.Info.Visible = false
	if status == "For Sale" then
		selectBtn.Text = "BUY"
		selectBtn.TextColor3 = colors["green"]
		selectBtn.UIStroke.Color = colors["green"]
		selectBtn.Info.Tickets.Image = "rbxassetid://9058199447"
		selectBtn.Info.Visible = true
		shopPreview.ImageColor3 = colors["neutral"]
	elseif status == "Owned" then
		selectBtn.Text = "SELECT"
		selectBtn.TextColor3 = colors["white"]
		selectBtn.UIStroke.Color = colors["white"]
		shopPreview.ImageColor3 = colors["white"]
	elseif status == "Selected" then
		selectBtn.Text = "SELECTED"
		selectBtn.TextColor3 = colors["red"]
		selectBtn.UIStroke.Color = colors["red"]
		shopPreview.ImageColor3 = Color3.fromRGB(255, 190, 190)
	elseif status == "Off-Sale" then
		selectBtn.Text = "OFF-SALE"
		selectBtn.TextColor3 = colors["black"]
		selectBtn.UIStroke.Color = colors["black"]
		shopPreview.ImageColor3 = Color3.fromRGB(0, 0, 0)
	end
end

function displayModel(model, modeType)
	if model then
		local status = checkStatus(activeScreen:FindFirstChild(model.Name))
		if status then
			setupPreview(status)
		end
		
		-- create a camera if it doesn't exist
		shopPreview:ClearAllChildren()
		local camera = Instance.new("Camera", shopPreview)
		local worldModel = Instance.new("WorldModel", shopPreview)
		
		local viewportCamera = shopPreview.Camera
		shopPreview.CurrentCamera = viewportCamera
		viewportCamera.Parent = shopPreview
		viewportCamera.FieldOfView = 40
		
		if modeType == "Skin" then
			-- nice easy copy and paste
			local viewModel = model:Clone()
			viewModel.Parent = shopPreview.WorldModel
			local cframe, size = viewModel:GetBoundingBox()
			
			local animFolder = ReplicatedStorage.Animations:FindFirstChild(viewModel.Name)
			local idleAnim = animFolder.Idle
			local idleTrack = viewModel.Humanoid.Animator:LoadAnimation(idleAnim)
			idleTrack:Play()
			viewportCamera.CFrame = cframe * CFrame.new(0,0,-10) * CFrame.Angles(0, math.rad(180), 0)
		end
		
		if modeType == "Traps" then
			local viewModel = model:Clone()
			viewModel.Parent = shopPreview.WorldModel
			
			local cframe, size = viewModel:GetBoundingBox()
			viewportCamera.CFrame = cframe * CFrame.new(0,0,-10) * CFrame.Angles(math.rad(45), math.rad(180), 0)
		end
		
	end
end

local function selectItem()
	if shopPreview:FindFirstChild("WorldModel") and shopPreview.WorldModel:FindFirstChildOfClass("Model") then
		
		local model = shopPreview.WorldModel:FindFirstChildOfClass("Model")
		local itemType = model.ItemType.Value
		local itemName = model.Name
		local status = checkStatus(shopMenu[activeScreen.Name]:FindFirstChild(itemName))
		
		if playerOwnsItem(itemType, itemName) then
			if status == "Owned" then
				local response = selectItemFunc:InvokeServer(itemType, itemName)
				
				if response == "Success" then
					updateStore()
				else
					if not response then response = "Error" end
					popupPrompt(response, false, "ShopMenu")
				end
			elseif status == "Selected" then
				updateStore()
			end
		elseif status == "For Sale" then
			-- Client-side check, also repeated on server
			if tonumber(selectBtn.Info.Status.Text) then
				local price = tonumber(selectBtn.Info.Status.Text)
				if tonumber(selectBtn.Info.Status.Text) > player.Tickets.Value then
					local diff = tonumber(selectBtn.Info.Status.Text) - player.Tickets.Value
					if diff < 50 then
						-- prompt to purchase 50 tickets
						local purchase = popupPrompt("Purchase more tickets?", true, "ShopMenu")
						if purchase then
							MarketService:PromptProductPurchase(player, 1245332333)
							updateStore()
						end
					elseif diff < 100 then
						-- prompt to purchase 100 tickets
						local purchase = popupPrompt("Purchase more tickets?", true, "ShopMenu")
						if purchase then
							MarketService:PromptProductPurchase(player, 1245332335)
							updateStore()
						end
					elseif diff < 150 then
						-- prompt to purchase 150 tickets
						local purchase = popupPrompt("Purchase more tickets?", true, "ShopMenu")
						if purchase then
							MarketService:PromptProductPurchase(player, 1245332334)
							updateStore()
						end
					else
						-- prompt to purchase 1000 tickets
						local purchase = popupPrompt("Purchase more tickets?", true, "ShopMenu")
						if purchase then
							MarketService:PromptProductPurchase(player, 1245332332)
							updateStore()
						end
					end
					
				else
					local confirm = popupPrompt("Purchase " .. itemName .. "?", true, "ShopMenu")
					if confirm then
						local response = buyItem:InvokeServer(itemName)
						if response == "Success" then
							local response = selectItemFunc:InvokeServer(itemType, itemName)
							
							if response == "Success" then
								updateStore()
							else
								updateStore()
							end	
						else
							if not response then response = "Error" end
							popupPrompt(response, false, "ShopMenu")
						end
					end
				end
			else
				popupPrompt("You have not unlocked this", false, "ShopMenu")
			end
		end
	end
end
selectBtn.Activated:Connect(selectItem)

function previewItem(btn, itemType)
	
	local status = checkStatus(btn)
	shop.Select.Info.Status.Text = btn.Info.Status.Text
	
	shop.Select.Visible = true
	
	-- make a copy of the model inside the viewport frame we just clicked
	local model = btn.ViewportFrame:FindFirstChildOfClass("Model")
	displayModel(model, itemType)
end

local function setupScreen(screen, dictionary, itemFolder)
	
	local columns = 6
	local padding = shopMenu.AbsoluteSize.X * 0.01
	local relativeCellSize = (1 / columns) - 0.01
	local absoluteCellSize = shopMenu.AbsoluteSize.X * relativeCellSize
	
	local UIGridLayout = Instance.new("UIGridLayout")
	UIGridLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
	UIGridLayout.SortOrder = Enum.SortOrder.LayoutOrder
	UIGridLayout.CellSize = UDim2.new(0, absoluteCellSize, 0, absoluteCellSize)
	UIGridLayout.CellPadding = UDim2.new(0, padding, 0, padding)
	UIGridLayout.Parent = screen
	
	local total = 0
	
	for index, item in pairs(dictionary)do
		total += 1
		
		local btn = shopMenu.Template:Clone()
		btn.Name = item.Name
		btn.Visible = true
		btn.LayoutOrder = item.Price
		btn.Parent = screen
		
		-- setup text info
		btn.Title.Text = item.Name
		btn.Info.Status.Text = item.Price
		
		if item["For Sale"] == false then
			btn.ViewportFrame.BackgroundColor3 = colors["black"]
		elseif item["Limited Edition"] then
			btn.ViewportFrame.BackgroundColor3 = colors["white"]
		end
		
		-- setup viewportframe
		local viewportCamera = Instance.new("Camera")
		btn.ViewportFrame.CurrentCamera = viewportCamera
		viewportCamera.Parent = btn.ViewportFrame
		viewportCamera.FieldOfView = 40
		
		local model
		-- skin
		if itemFolder == skinFolder then
			model = itemFolder[item.Name]:Clone()
			local cframe, size = model:GetBoundingBox()
			viewportCamera.CFrame = cframe * CFrame.new(0,0,-10) * CFrame.Angles(0, math.rad(180), 0)
			-- traps
		elseif itemFolder == trapFolder then
			btn.Title.Text = item.Name .. "(" .. item.Duration .. "s)"
			model = itemFolder[item.Name]:Clone()
			local cframe, size = model:GetBoundingBox()
			viewportCamera.CFrame = cframe * CFrame.new(0,0,-10) * CFrame.Angles(45, math.rad(180), 0)
		end
		
		if model then
			local itemType = Instance.new("StringValue")
			itemType.Value = item.Type or itemFolder.Name
			itemType.Name = "ItemType"
			itemType.Parent = model
			
			model.Parent = btn.ViewportFrame
			
			btn.Activated:Connect(function()
				previewItem(btn, itemType.Value)
			end)
		end
	end
	
	local rows = math.floor(total / columns, 0.5) + 1
	local height = (absoluteCellSize + padding) * rows
	screen.CanvasSize = UDim2.new(0, 0, 0, height)
end

function setupCharacterShop()
	
	loadData()
	-- create buttons for every skin and trap
	setupScreen(skinScreen, availableCharacters, skinFolder)
	setupScreen(trapScreen, availableTraps, trapFolder)
end

-- buy 50 tieces
homeScreen.Tickets50.Activated:Connect(function()
	MarketService:PromptProductPurchase(player, 1245332333)
end)

-- buy 100 tieces
homeScreen.Tickets100.Activated:Connect(function()
	MarketService:PromptProductPurchase(player, 1245332335)
end)

-- buy 150 tieces
homeScreen.Tickets150.Activated:Connect(function()
	MarketService:PromptProductPurchase(player, 1245332334)
end)

-- buy 1000 tieces
homeScreen.Tickets1000.Activated:Connect(function()
	MarketService:PromptProductPurchase(player, 1245332332)
end)

-- buy random tieces
homeScreen.RandomTickets.Activated:Connect(function()
	MarketService:PromptProductPurchase(player, 1245337437)
end)

local function buyGamepass(id)
	local hasPass = false
	
	local success, err = pcall(function()
		hasPass = MarketService:UserOwnsGamePassAsync(player.UserId, id)
	end)
	
	if not success then
		warn("Error getting gamepass info for", player.Name, err)
		popupPrompt("Error getting gamepass info", false)
		return
	end
	
	if hasPass then
		popupPrompt("You already own this", false)
	else
		-- player does not own the game pass; prompt them to purchase
		MarketService:PromptGamePassPurchase(player, id)
	end
end

-- double chances
homeScreen.DoubleChances.Activated:Connect(function()
	buyGamepass(32648691)
end)

-- double tickets
homeScreen.DoubleTickets.Activated:Connect(function()
	buyGamepass(32288239)
end)

shop:GetPropertyChangedSignal("Visible"):Connect(function()
	-- update the store page when opened
	if shop.Visible then
		updateStore()
	else
		-- hide any popups when closed
		popup.Visible = false
	end
end)

-- initial setup
setupCharacterShop()
updateStore()

Explorer:

Please help…

what’s line 243. i need to know that first.

Edit: actually i think i found it. btn doesnt exist. go back to showScreen() function and find out which findfirstchild is nil.

local function checkStatus(btn)
	if btn.Info.Status.TextColor3 == colors["red"] then
		return "Selected"
	elseif btn.Info.Status.TextColor3 == colors["green"] then
		return "Owned"
	elseif btn.Info.Status.TextColor3 == colors["yellow"] then
		return "For Sale"
	elseif btn.Info.Status.TextColor3 == colors["neutral"] then
		return "Off-Sale"
	end
end

This function here is causing their issue.

The function is called three times throughout the script.
local status = checkStatus(activeScreen:FindFirstChild(model.Name))
local status = checkStatus(shopMenu[activeScreen.Name]:FindFirstChild(itemName))
local status = checkStatus(btn)

I can only presume one or more of those calls is passing a nil value instead of the intended value.

it says the error is in this function

function previewItem(btn, itemType)
	
	local status = checkStatus(btn)
	shop.Select.Info.Status.Text = btn.Info.Status.Text
	
	shop.Select.Visible = true
	
	-- make a copy of the model inside the viewport frame we just clicked
	local model = btn.ViewportFrame:FindFirstChildOfClass("Model")
	displayModel(model, itemType)
end