The other answers to this question are indeed telling you the mistake you made, but it does not look like they are being quite clear enough on how exactly to apply the fix. In short, all you need at the very least is an and
operator. However, I am going to go a little more in depth than that because I feel that this question stems from a lack of understanding of datastores. Let's go over this.
02 | local success, err = pcall ( function () |
03 | data = dataStore:GetAsync(plr.UserId) |
04 | print ( "getting data..." ) |
10 | print ( "The player has no data!" ) |
When making a datastore call like the above, the 'success' variable just indicates if the call made it over the network. So, you could ask for information from a datastore and you may find that the call is successful, but it doesn't mean that there is actually any data saved.
To elaborate on the why a datastore request might fail sometimes (and you might already know this!), when you ask for something over the network such as datastore information, that request has to make it over to Roblox's servers and hence could fail at times, usually because of a bad connection. The success variable tells you if it failed or not and that's its job. Again, it doesn't actually indicate that there is any saved data.
What you want to do is check if the call was successful AND if data was returned, and only then update your value object with the returned data.
1 | if success and data then |
4 | print ( "The player has no data OR the datastore request failed!" ) |
That's the fix. Very simple. Now, if you wish to handle datastore call failure in some way (retrying the request some number of times, etc.) you could take this a step further by firstly checking if the call was successful, then checking for the existence of saved data, like so:
02 | print ( "The datastore request was successful." ) |
05 | print ( "Saved data found" ) |
07 | print ( "No saved data found" ) |
10 | print ( "The datastore request failed: " ..err") |