Why does GlobalDataStore::GetAsync return nothing instead of nil when there is no data?

So I read a post on scripting helpers a few months ago (can’t remember which one) where it was mentioned that :GetAsync returns nothing, not even nil, when there is no data. Which surprised me since it is convention that nil is returned if nothing useful is able to be returned.

Functions like Instance::FindFirstChild(string name[, bool recursive]), Instance::FindFirstAncestor(string name), Instance::WaitForChild(string name[, double timeout]) are a few examples that return nil if no instance called name is found. So why does GlobalDataStore::GetAsync return nothing?

If you execute this in command bar, assuming you don’t have a data store named "test" and therefore no data on the key "hi" you will get 0 in the output which means :GetAsync returned nothing. Not even nil.

print(select("#", game:GetService("DataStoreService"):GetDataStore("test"):GetAsync("hi")))

I’d move this to #platform-feedback:studio-features or #platform-feedback:studio-bugs if I were you.

This could be intentional. The post I read was made 6 years ago. It isn’t necessarily an issue but I was wondering why, since getter functions always return something. That is their nature. Btw that would be in engine bugs

Well then, I’d move this to #platform-feedback:studio-features still.

Normally when u call :GetAsync on something you don’t saved it whould return nil.

1: You saved “” to “Test”
2: You need to move this to #platform-feedback:studio-bugs because this is not happening to me.

The nil is from the variable declaration and not the function call itself. It is not a bug at best it is just api inconsistency.

2 Likes

That means :GetAsync is returning absolutely nothing.

That means :FindFirstChild returned one value. Obviously nothing is called "lol" in the data model on my end so it returned nil at least.

I understand there is no “lol” in the game. And that is why I used that example. nil is different from nothing. Nil is a datatype. That is why select returned 1 since nil is not absolutely nothing.

What happens if you have the datastore test but don’t have the hi key in the datastore?

There would be an error because there would be no key to get data from…

No, it should return nil then according to the documentation.

No, you’ve misinterpreted the docs. It is meant to say if there is no value in data stores for that key then it would return nil. But this is in fact not the case. Just because what the docs says it returns does not match what it actually does return (nothing) I might as well make a bug report for it.

No, you didn’t understand what I was talking about. I meant that the datastore exists but hi key in the datastore has not been set yet.

Exactly. That is why I am wondering why it returns “void” instead of nil.

Then that shouldn’t be posted on scripting support because it turns out that we are not able to solve the problems that lay in the engine.

I posted this looking for feedback if this was a bug or intentional. I see no need to post a bug report on intended behaviour. I will assume it is the former for now.

It appears this is a bug, albeit not one of huge concern.

For all intents and purposes, GetAsync returns nil when there is no value set. Like you mentioned, you can think of the function as just returning nothing (different from returning nil) when no value is set.

This behavior is very prevalent in common code. Consider a function like this:

local function test()
-- return a value if everything goes as planned, if not, just let the function reach the end (return nothing)
end
print(select('#', test()))   --> 0
print(type(test()))          --> error

When you compare the value to nil, it returns true. If you run an if statement to check if the value exists (positive outcome), it will return false. Thus, it behaves, for the most part, like nil. The only caveat is, like you observed, there is literally nothing returned. Thus, calling select will result in 0 arguments, and calling a function like type() will error because it expects a parameter but it gets nothing.

It should also be noted that the moment you assign the result from a function that doesn’t return anything (like GetAsync when no value is set or the function shown above) to a variable, that variable is not ‘nothing’ but nil, so it will result in what you’re looking for with select.

local a = game:GetService("DataStoreService"):GetDataStore("test"):GetAsync("hi")
print(select('#', a))  ---> 1
print(type(a))         ---> nil
1 Like

For the last part that is what I tend to do. Store the result of function calls in variables. I do believe this is just API inconsistency so I’m gonna make a small bug report about it. Of course it is nothing big but who doesn’t want consistency

Yeah, for sure.

It could potentially benefit a new learner if they don’t have to deal with subtle inconsistencies like this.

Speaking of which, the good thing is that voided values just turn into nil when assigning to something and when they are behind a comma. Voided values in lua are particularly funny because they only exist temporarily as a result of not actually having anything in place of the return value, meaning that the only problems that would really be caused arise from things like counting/iterating through return values and printing directly to the output, which is usually something you never do with GetAsync anyways.

1 Like