Store Operations Globally makes Lua Error

Hi! I’m using the latest version of Tradeskillmaster addon, when I check the box “Store Operations Globally” an error window of the game pops up:

Message: Interface\SharedXML\Util.lua:191: bad argument #1 to 'pairs' (table expected, got nil)
Time: Sat Mar  9 22:06:12 2019
Count: 1
Stack: Interface\SharedXML\Util.lua:191: bad argument #1 to 'pairs' (table expected, got nil)
[C]: ?
[C]: in function `pairs'
Interface\SharedXML\Util.lua:191: in function `CopyTable'
...ns\TradeSkillMaster\Core\Service\Operations\Core.lua:75: in function `SetStoredGlobally'
...TradeSkillMaster\Core\UI\MainUI\Settings\General.lua:419: in function `callback'
...adeSkillMaster\Core\UI\Elements\ApplicationFrame.lua:725: in function `_onClickHandler'
...s\TradeSkillMaster\Core\UI\Elements\ActionButton.lua:258: in function <...s\TradeSkillMaster\Core\UI\Elements\ActionButton.lua:246>

Locals: 

Is there something I can do about this? I haven’t found any information related to this issue. I’ll appreciate any help or guidance. Thanks in advance.

Report the error to the TSM authors. The error is complaining that TSM is not passing an expected table to the Blizzard CopyTable function.

Thanks for answering, I did that the day before yesterday and Gumdrops told me it has nothing to do with the addon, that was an issue of the game.

TSM.db.global.userData.operations = CopyTable(TSM.db.profile.userData.operations)

An addon passing nil to a function that expressly say it’s purpose is to copy a table hardly seems to be the functions fault.

Strictly speaking CopyTable is in an addon.

It’s one of Blizzard’s called Utils.

And I have an improved copy that does rudimentary data checking.

That function is pretty poorly written that it can’t do that.

But then, it’s a Blizzard addon.

I’m not sure of the relevance of the function being in/out of an addon or being created by Blizzard or anyone else.

CopyTable does what it says it does, copies tables by accepting one parameter (a table) that is not nilable. As with any other function in any language or API, it is the responsibility of the programmer to conform to it’s signature.

You don’t always know if a result from something that returns a table will return anything at all.

Many of Blizzard’s such functions don’t return an empty table, they return nil.

I wrote my wrapper to deal with that.

A robust CopyTable function would handle a nil input result or non-table input more gracefully than what Blizzard wrote.

All I did was wrap it in code that does that.

I’ve also got a parm in it that will either report the error or will return an empty table or single-entry table depending on what I need.

Best I can recall, this is it:

function xCopyTable(inData, forceToTable)

  if inData and type(inData) == "table" then

    return CopyTable(inData)

  elseif inData and forceToTable then

    return CopyTable({inData})

  elseif forceToTable then

    return CopyTable({})

  elseif inData then

    print("Bad data passed to table copy function: '"..
          tostring(inData).."' (not a table).")

  else

    print("Bad data passed to table copy function: nil.")

  end

end

The problem here is not the return but the input to the CopyTable function.

Your function still won’t work without valid input. You decided to print a message rather than generate an error and both say the same thing. In either case, the programmer that asks either function to copy nil is doing it wrong.

Fizzie, if I had control of the API, you’re completely correct.

The problem is that Blizzard’s API isn’t alway consistent in how it deals with no data.

Sometimes you get an empty table. Sometimes you get an actual boolean result. Sometimes you get a nil.

None of that is well documented.

I did it in self defense against the multitude of Blizzard stupidity out there.

Virtually every other language I’ve used that had anything remotely like the structure Lua has had either a set-value-to-this function or an override available for that function that could handle any data type.

I just did what was needed to avoid a hard error.

All I have to do with my function is test that the return results are in fact a table.

Table - got a good copy.
Not a table - nothing was there.

Additionally, in some instances I need to put something into a table that isn’t. There are times when you get, for instance, one power type back for a given unit type and times when you get more than one (this is just an example and not a particularly well thought-out one). If I’m trying to code for the general case, I need to code for tables. But it makes little sense to look up the available power types for things like Hunters or Warrior because they only ever have one active. I table those up as single value items.

Dealing with Druids is more complicated.

But rather than have completely separate processes for Druids and Hunters (again, for example) I have one and it drives from tabled data.

The process that determines what power type is appropriate doesn’t know until run time which class is being run and is adaptable so it grabs the data and the uses xCopyTable to put it into the common table format that I use elsewhere.

Having ONE “copy the power type” process that always produces tabled output regardless of the input is useful.

There are many, many places where this is true.

Occasionally, I get a nil value in when I can’t process it. Rather than get a hard error, I control the error by testing for it on output.

I might not know until run time that I’ll GET a nil output. The print functions you saw there are far less than what I’m actually doing. I maintain an internal error table. What really gets returned is a reference to that table if an error occurs.

I might choose to print that very message in debugging mode. Generally what happens is that my code recognizes a case that is outside of expected norms.

Things like a character that hasn’t yet reached a point where it can choose a specialization, for instance, or Pandaren who haven’t yet chosen a Faction. 99.9% of the play life of that toon, that won’t be an issue, but for the 0.1% that it is, I don’t want my addon puking in an ungraceful way.

Essentially this is so I can control the failure.

Yes, if I have to have data, it will fail.

But having to code in 1,000 place the “is there data here and is it in the right format” is just asking for errors.

I put all of that in one place and if it throws an error, THEN I branch to deal with it.

I’m still not sure what your point here is. Defend Gumdrops or stick the middle digit up at Blizzard because…

You wrapped extra stuff around a call to pairs() to make sure it didn’t error, you didn’t stop anyone passing in nil and, (if the code you posted is correct) getting back nil

Once you’ve gotten passed:

else
    print("Bad data passed to table copy function: nil.")

That function still returns nil and allows you to go on unawares if you didn’t notice the print(). I’m not sure which is worse, telling you you’re an idiot and making you do something about it or just let you go on being an idiot with other possible consequences later.

When it’s all said an done, the problem wasn’t/isn’t with the CopyTable function (Blizzard) complaining about being give a nil it never should have been passed.

I don’t know if your addon is out in the wild or not but if so and someone came to you and said “I got this error:”

"Bad data passed to table copy function: nil."

Would your response be “That’s a Blizzard problem”

Again, you’re taking those print statements as gospel.

I mentioned that I don’t actually do that.

What I do is a whole module full of error checking and type checking code that just wouldn’t make sense here.

What I’ve done is removed the need to worry about whether or not Blizzard’s API is returning consistent data or not.

I do it with that function and six or seven others that are just utilities.

I’ve got a streamlined sort function that takes either tables or (…) arguments (or both), for example.

For me, it’s about predictability and consistency, neither of which are hallmarks of Blizzard’s code.

Probably 90% of the time I could get away with just using CopyTable.

It’s more trouble to sort out which 10% needs more robust error checking and broad-spectrum type handling than it is to just use my version everywhere.

As far as getting a nil value where I expected to see data? Not necessarily a Blizzard problem. As mentioned, there are edge cases where relatively basic data, data that you would 99.9% of the time be absolutely certain was there, won’t be. I mentioned two (Pandarens toons, pre-faction choice and most classes pre-specialization choice). There are others. I won’t list them all. I probably haven’t figured them all out quite yet. But there are and the existing API tools don’t handle them particularly gracefully.

I would much prefer to have the ability to gracefully detect when something has stepped out of the general case into something not coded for than to have it simply report an error through BugSack/BugGrabber.

If, for instance, I was attempting to determine whether a particular mount was available to a toon and that toon had not yet chosen a faction (it can happen, there’s some dude who leveled a Pandaran to max never leaving the islands and never picking a faction) and that toon was attempting to choose a mount that required a faction (or was limited to a faction) there might be an issue. Because virtually every OTHER toon is going to have a faction already, I don’t include the “factionless” filter in my code (an optimization choice). If this caused an issue at runtime, I’d give a more detailed message to the user (or branch to a different bit of code that handles that edge case, more likely).

If this were a situation where a too-low-level toon was attempting to access something that required a specialization selection, in that case they’d get a very specific message telling them what was wrong.

What I would not do is code for the general case and leave the edge cases unhandled. I ran into one of those with DBM just today. I’m still not sure what the problem was but one of the DBM modules was causing a near instant disconnect on a new, first-level undead toon I created to test something else. No error message printed. Not even Bugsack could tell me more than just “3 x nil” as a reason. I only figured out which addon it was by turning them off one at a time. And DBM is one of the better-maintained add-ons out there. At a minimum, every error point I’ve been able to identify produces a detailed error message giving the add-on name, module name, function name, and input parameters and in come cases a snapshot of tabled data about the toon in question is stored away for later analysis.

I’m sorry you feel the need to denigrate me for expanding on the tools in the API that Blizzard has given us and for speaking about both the shortcomings of the Blizzard API and strategies for getting around them, but it’s late and everyone has a bad day now and then.

You’ve been a big help to me in sorting out how all this stuff works and working with you in the past has been something that made getting this far possible.

I’m certainly not trying to denigrate anyone and my apologies if you feel that is the case. I’m just trying to figure out what your original and subsequent posts are trying to tell the OP about the error they reported and what the problem/fix might be.

Beyond a change to the TSM code to actually pass a table, I don’t see what that can be.

Thank you guys for sharing your points of view, I’m learning a lot on this forum. Is there something I can do to help solve this?

Perhaps I misread that.

I always assume the debugger is telling me I’m an idiot whenever I hear “Fatality”. Turns out it is usually right.

In this case, you is a person receiving a message from a debugger, not you personally. Feel free to replace “you” with “me” and “you’re” with “I’m” in that sentence.

I tend to try for humor, I don’t always succeed. Maybe I should have emojied as well :laughing:

Works for me.