Error: "attempt to index field "?" a nil value", but only when I am on a certain scene

So I am making a game which has a lot of values to save. I used a module by Rob Miracle with makes it easy to save and load tables to do this with two tables. One is named “gamestuff” and the other is named “leveltries”.

In one scene I declare the values of these tables (only if they haven’t been saved before)

gamestuff.level = 1
        gamestuff.sound = true
        gamestuff.name = “user”
        gamestuff.highscore = 0
        gamestuff.lastscore = 0
        gamestuff.volume = 0.5
        gamestuff.lastlevel = 1
        gamestuff.won = false
        gamestuff.bouncesleft = 0
        
        local leveltries = {
            {tries =0, wins = 0, fails = 0, retries = 0, bouncesleft = 0, bestbounces = 0, score = 0},
            {tries =0, wins = 0, fails = 0, retries = 0, bouncesleft = 0, bestbounces = 0, score = 0},
            {tries =0, wins = 0, fails = 0, retries = 0, bouncesleft = 0, bestbounces = 0, score = 0},
            {tries =0, wins = 0, fails = 0, retries = 0, bouncesleft = 0, bestbounces = 0, score = 0},
            {tries =0, wins = 0, fails = 0, retries = 0, bouncesleft = 0, bestbounces = 0, score = 0},
            {tries =0, wins = 0, fails = 0, retries = 0, bouncesleft = 0, bestbounces = 0, score = 0},
            {tries =0, wins = 0, fails = 0, retries = 0, bouncesleft = 0, bestbounces = 0, score = 0},
            {tries =0, wins = 0, fails = 0, retries = 0, bouncesleft = 0, bestbounces = 0, score = 0},
            {tries =0, wins = 0, fails = 0, retries = 0, bouncesleft = 0, bestbounces = 0, score = 0},
            {tries =0, wins = 0, fails = 0, retries = 0, bouncesleft = 0, bestbounces = 0, score = 0},
            
  
        }
        loadsave.saveTable(leveltries, “leveltries.json”)
        loadsave.saveTable(gamestuff, “gamestuff.json”)

This works fine except for when I try to go to a restart level scene and a win scene after the first level.

But it confuses me because in level one I try

        print("Fails: "…leveltries[gamestuff.lastlevel].fails)

and it works

But when I try it in the restarter scene, it doesn’t:

local storyboard = require( “storyboard” )
local scene = storyboard.newScene()
local loadsave = require(“loadsave”)

local gamestuff = {}
local leveltries = {}
local options =
{
    effect = “fade”,
    time = 400
}

local function loader()
    gamestuff = loadsave.loadTable(“gamestuff.json”)
    leveltries = loadsave.loadTable(“leveltries.json”)
    print(“loaded”)
end

local function save()
    loadsave.saveTable(gamestuff, “gamestuff.json”)
    loadsave.saveTable(leveltries, “leveltries.json”)
end

function scene:createScene(event)
    
    gamestuff = {}
    leveltries = {}
end

function scene:enterScene(event)
    
    loader()
    print(“Fails”…leveltries[gamestuff.lastlevel].fails) --says the error is here
    local a = leveltries[gamestuff.lastlevel].fails + 1
    leveltries[gamestuff.lastlevel].fails = a
    timer.performWithDelay( 50, function()
        save()
        storyboard.gotoScene(“level”…tostring(gamestuff.lastlevel), options)
    end, 1 )
    
end

function scene:exitScene(event)
    local group = self.view
    storyboard.removeScene(“restarter”)

end

function scene:destroyScene(event)
    gamestuff = nil
    leveltries = nil
    options  = nil
end
scene:addEventListener( “createScene”, scene )
scene:addEventListener( “enterScene”, scene )
scene:addEventListener( “exitScene”, scene )
scene:addEventListener( “destroyScene”, scene )

return scene

Can anyone tell my why the code above doesn’t work?

Could it be that I don’t properly remove the “gamestuff” and “leveltries” in level1?

Why don’t you put some prints in and see what the value of gamestuff is and gamestuff.lastlevel are?

Okay. Look at the line number in the error message which will tell you where it is failing. My vote goes for 

leveltries[gamestuff.lastlevel].fails

As the problem. What that error message usually means is that level tries does not have a gamestuff.lastlevel member. So because this is an associative array, leveltries[gamestuff.lastlevel] returns nil. So  you are trying to do, effectively.

nil.fails

and as RobM suggests, something like

print(leveltries,gamestuff.lastlevel,level tries[gamestuff.lastlevel])

before the error should let you check whether this is correct or not. 

I would also look into the use of asserts. One of the problems with lua is that there is no typing or type control (really) so your data structures can get into a muddle. So if you write something like 

assert(level tries[gamestuff.lastlevel] ~= nil)

before you try to access it, it gives an extra level of protection - basically it checks the statement is true, and throws an error if not.

I tried to put some prints in. In the level 1 scene:

Everything works:

print(gamestuff.lastlevel)

print(leveltries[gamestuff.lastlevel].fails)

print(leveltries[gamestuff.lastlevel])

However in the restarter scene, only:

print(gamestuff.lastlevel) --which prints 1 and is right

works

print(leveltries[gamestuff.lastlevel]), in the restarter scene prints nil

I don’t know how this is possible. I load the same tables in the same way at the same time in both scenes and used them in the same way.

Could it be that I am removing the tables incorrectly?

In scene one these are my exitScene and removeScene:

function scene:exitScene(event)
    Runtime:removeEventListener(“touch”, shoot)
    menub:removeEventListener(“touch”, gotomenu)
    restartb:removeEventListener(“touch”, restart)
    goal:removeEventListener(“collision”, win)
    ball:removeEventListener(“collision”, hit)
    ball:removeEventListener(“collision”, grow)
    Runtime:removeEventListener(“enterFrame”, twinkle)
    Runtime:removeEventListener(“enterFrame”, redraw)
end

function scene:didExitScene(event)
    --storyboard.removeScene(“level1”)
end

function scene:destroyScene(event)
    bg:removeSelf()
    ball:removeSelf()
    goal:removeSelf()
    topwall:removeSelf()
    bottomwall:removeSelf()
    rightwall:removeSelf()
    leftwall:removeSelf()
    menub:removeSelf()
    bounces = nil
    bouncestext:removeSelf()
    taps = nil
    restartb:removeSelf()
    tuttext:removeSelf()
    gamestuff = nil
    
    bg = nil
    ball = nil
    goal = nil
    topwall = nil
    bottomwall = nil
    rightwall = nil
    leftwall = nil
    menub = nil
    bouncestext = nil
    restartb = nil
    tuttext = nil
    options = nil
    trailprops = nil
    for i = 1, 3 do
        wallColor[i] = nil
    end
    
    
end

scene:addEventListener(“createScene”, scene)
scene:addEventListener(“enterScene”, scene)
scene:addEventListener(“exitScene”, scene)
scene:addEventListener(“didExitScene”, scene)
scene:addEventListener(“destroyScene”, scene)

return scene

Please help!

Is this question too hard without having access to the the full code?

The problem you’re going to find is that you can post too little code and people can’t help you.  Post too much and it overwhelms everyone, specially when you are not using and tags (without the spaces) to have it syntax highlight and indent correctly. 

I see you nil your gamestuff table in your destroy scene, but there isn’t enough here to see where you create the table, if it’s global or local and what other scene’s might be doing to it.  So yes, we need to see more code.

Rob

Why don’t you put some prints in and see what the value of gamestuff is and gamestuff.lastlevel are?

Okay. Look at the line number in the error message which will tell you where it is failing. My vote goes for 

leveltries[gamestuff.lastlevel].fails

As the problem. What that error message usually means is that level tries does not have a gamestuff.lastlevel member. So because this is an associative array, leveltries[gamestuff.lastlevel] returns nil. So  you are trying to do, effectively.

nil.fails

and as RobM suggests, something like

print(leveltries,gamestuff.lastlevel,level tries[gamestuff.lastlevel])

before the error should let you check whether this is correct or not. 

I would also look into the use of asserts. One of the problems with lua is that there is no typing or type control (really) so your data structures can get into a muddle. So if you write something like 

assert(level tries[gamestuff.lastlevel] ~= nil)

before you try to access it, it gives an extra level of protection - basically it checks the statement is true, and throws an error if not.

I tried to put some prints in. In the level 1 scene:

Everything works:

print(gamestuff.lastlevel)

print(leveltries[gamestuff.lastlevel].fails)

print(leveltries[gamestuff.lastlevel])

However in the restarter scene, only:

print(gamestuff.lastlevel) --which prints 1 and is right

works

print(leveltries[gamestuff.lastlevel]), in the restarter scene prints nil

I don’t know how this is possible. I load the same tables in the same way at the same time in both scenes and used them in the same way.

Could it be that I am removing the tables incorrectly?

In scene one these are my exitScene and removeScene:

function scene:exitScene(event)
    Runtime:removeEventListener(“touch”, shoot)
    menub:removeEventListener(“touch”, gotomenu)
    restartb:removeEventListener(“touch”, restart)
    goal:removeEventListener(“collision”, win)
    ball:removeEventListener(“collision”, hit)
    ball:removeEventListener(“collision”, grow)
    Runtime:removeEventListener(“enterFrame”, twinkle)
    Runtime:removeEventListener(“enterFrame”, redraw)
end

function scene:didExitScene(event)
    --storyboard.removeScene(“level1”)
end

function scene:destroyScene(event)
    bg:removeSelf()
    ball:removeSelf()
    goal:removeSelf()
    topwall:removeSelf()
    bottomwall:removeSelf()
    rightwall:removeSelf()
    leftwall:removeSelf()
    menub:removeSelf()
    bounces = nil
    bouncestext:removeSelf()
    taps = nil
    restartb:removeSelf()
    tuttext:removeSelf()
    gamestuff = nil
    
    bg = nil
    ball = nil
    goal = nil
    topwall = nil
    bottomwall = nil
    rightwall = nil
    leftwall = nil
    menub = nil
    bouncestext = nil
    restartb = nil
    tuttext = nil
    options = nil
    trailprops = nil
    for i = 1, 3 do
        wallColor[i] = nil
    end
    
    
end

scene:addEventListener(“createScene”, scene)
scene:addEventListener(“enterScene”, scene)
scene:addEventListener(“exitScene”, scene)
scene:addEventListener(“didExitScene”, scene)
scene:addEventListener(“destroyScene”, scene)

return scene

Please help!

Is this question too hard without having access to the the full code?

The problem you’re going to find is that you can post too little code and people can’t help you.  Post too much and it overwhelms everyone, specially when you are not using and tags (without the spaces) to have it syntax highlight and indent correctly. 

I see you nil your gamestuff table in your destroy scene, but there isn’t enough here to see where you create the table, if it’s global or local and what other scene’s might be doing to it.  So yes, we need to see more code.

Rob