Pcalls - When and how to use them

Intoduction

Protected calls, formally known as pcalls, can run functions without erroring and provide if your code was successfully ran but what’s the best way to use them and when should I?

In this tutorial I will be covering the entirety of pcalls and where you’ll need them, so lets begin!

Pcall basics

First and foremost, we should learn what pcall’s syntax really looks like:

pcall(f : function, ...args : any)

Basically, you pass your desired function in as argument 1 and any arguments you want to pass to that function that’s going to be pcall’d as argument 2, 3, 4 etc - Pcall doesn’t mind how many arguments you want to pass to it!

Onto some usecases now.

DataStore errors - Getting/Setting

Roblox DataStoreService, wonderful thing it is saving data between playing sessions.
There are some issues though when trying to Set/Get data which could even lead to data loss - no one wants to join a game and not have their data saved or retrieved.

This is where pcalls come in, you can use them to set/get data and check if it really did.
Commonly people do:

local DataStore = game:GetService("DataStoreService"):GetDataStore("MyData")
local data
local success, response = pcall(function()
    data = DataStore:GetAsync("key")
end)

Now this works however you’re creating a variable and a function everytime, when you can reduce that.

Here’s an example of that, I’ll run through it below:

local DataStore = game:GetService("DataStoreService"):GetDataStore("MyData")
local success, response = pcall(
    DataStore.GetAsync, --1
    DataStore, --2
    "key" --3
)

1 - The function for the pcall to run, you can’t call : functions inside a pcall therefore you have to call it with . and now the next arguments are what to pass to the function.
2 - Since you called a : function with . you need to supply what the function belongs to (self) as per OOP rules.
3 - The key of what to Get.

Now the first returned of pcall is a boolean to say if it succeeded, the second however is different;
If it didn’t succeed then it’ll be the error message as a string, you can use that to log why it failed.
If it did succeed then it’ll be what the pcall’d function returned, there can be more than just 1 returned value.

Now setting is very similar except obviously you provide what to set the key to.
Here’s an example of it:

local DataStore = game:GetService("DataStoreService"):GetDataStore("MyData")
local success, response = pcall(DataStore.SetAsync, DataStore, "key", "value")

This also works for UpdateAsync and IncrementAsync.

DataStore errors - retrying

Now since you know how to get/set using pcalls, you’ll need to know how to retry.
This can be achieved using a while or repeat loop, I’ll run through both.

Here’s an example of a while loop, I will talk through it afterwards:

local DataStore = game:GetService("DataStoreService"):GetDataStore("MyData")
local success, response --Don't need to define yet
local count = 0 --1

while not success do --2
    if count >= 1 then
        warn("Retrying, count:", count, "\nError:", response) --3
        wait(7) --4
    end

    success, response = pcall(DataStore.GetAsync, DataStore, "key") --5
    count = count + 1 --6
end

1 - This’ll be our count of how many times we’ve retried.
2 - While success isn’t true then.
3 - This’ll warn us that it’s retrying to get the data.
4 - A cooldown, DataStore limitations.
5 - Set success and data to what the pcall we try returns, do not localise it - the while loop won’t stop trying when it is true since it can’t see the local version of our success.
6 - Increase the count of times we’ve tried

Now a repeat loop will look very similar to this, here’s an example:

local DataStore = game:GetService("DataStoreService"):GetDataStore("MyData")
local success, response
local count = 0

repeat 
    if count >= 1 then
        warn("Retrying, count:", count, "\nError:", response)
        wait(7)
    end

    success, response = pcall(DataStore.GetAsync, DataStore, "key")
    count = count + 1
until success

The choice is up to you if either you use repeat loops or while loops, personally I prefer while loops.
Obviously you’ll need to do the same for any other datastore functions.

Roblox web API calls

Other than DataStores, there’s cases in the API were you’ll need to use pcalls, for example GetUserThumbnailAsync has to retrieve the thumbnail off the website - it’s a good idea to pcall if it fails.

StarterGui SetCores

If you’ve used SetCore before you may have seen the ‘<SetCore> has not been registed by the CoreScripts’, this is because the SetCores take time to be initialized therefore you’ll need to pcall it and keep trying.

Here’s a small example:

local sg = game:GetService("StarterGui")
local success
while not success do
    success = pcall(sg.SetCore, sg, "TopbarEnabled", false)
    wait(1) --add a wait to ensure no Game script timeouts :P
end

xpcall

The Lua global xpcall allows for you to provide a error handler as the second argument.
The parameter to the error handler will be the error message too!
Keep in mind xpcall cannot yield, do not call any wait()s etc inside one.
Here’s an example using SetCore:

local sg = game:GetService("StarterGui")
local success

function displayError(err)
    warn("An error occurred:", err)
end

while not success do 
    success = xpcall(sg.SetCore, displayError, sg, "TopbarEnabled", false)
    wait(1)
end

ypcall

Back in the day, pcalls couldn’t yield so Roblox created the yielding protected call.
This is now useless as pcalls can yield nowadays, you can use it if you’d like to however it makes no improvement.

debug.traceback

When an error happens, if you’d like to know where exactly it happened then use debug.traceback() and it’ll return the current stack of where you called it as a string.
Here’s a small example:

local sg = game:GetService("StarterGui")
local success

function displayError(err)
    warn("An error occurred!\nError:", err, "\nTraceback:", debug.traceback())
end

while not success do
    success = xpcall(sg.SetCore, displayError, sg, "ResetButtonCallback", false)
    wait(1)
end

Moving on from here

Now that you’ve learnt and hopefully understood the process of pcalls and retrying you can ensure no unexpected API errors which could’ve been stopped!

Helpful links

These links can help you if you still need to learn more or if you’d just like to read more about pcalls:

debug.traceback:

Every function you’ll need to pcall

There may be more than this, but here’s a list of functions that will need to be pcall’d:

List

Thanks for reading!

This was my second community tutorial, hope you did enjoy.
Please make sure to reply if you have anything to add or even correct me!

234 Likes
What is pcall()?
My DataStore randomly failed?
Are There Any Good Tutorials On "PCalls"?
DataStore not working
Scripting Resources Mega List
Pcall(), xpcall() and ypcall() Tutorial - How do they work?
How to add a filter to a command?
Data stores can't handle my request or?
Disabling the Reset Character button
How to prevent script execution time error?
Setting saved DataStore value
Leaderstats Script Not Working. Please Help!
Script Counter Issues
What is pcall? What does it do?
Is a PCALL neccessary here? (ProfileService)
Creating A Chat Command System Similar to a Discord Bot: Remote Events, :SetCore(), And More!
Weird error with UserOwnsGamepassAsync
Saving data module
Datastore Glitch?
Custom face Gui has a Strange Problem
Turning all buttons the color (Rebirth Help)
Roblox Scripting Roadmap & Learning Resource List
Datastore working in Roblox Studio but fails in game
[Closed] ReturnedTrue - programmer
How to make a Strong DataStore
Can someone help me fix this mixup with datastore?
Incrementing to a data store breaks my game loop
Coroutine not working
Issue with DataStores
How intensive is pcall()?
How to run 2 pieces of code at once? (How to reset server script)
How to make a Strong DataStore
How to make a Strong DataStore
How do I receive the table from a RemoteEvent
Pcall Necessity
How do I receive the table from a RemoteEvent
TeleportService cannot cast string into int64
Error Finding Script
How can I improve this datastore?
Attempt to call a nil value
How do I use Data stores?
Is pcall actually useful outside of datastores?
Tool DataStore not working
Output editing help
Can pcalls be used for anything other than datastorage?
How can i detect Notifications
How to make pcall datastore?
Datastore script is not working
This is pretty much a game breaking bug
DataStore request was added to queue. If request queue fills, further requests will be dropped
DataStore Not working
Help With Currency system
How can I avoid "is not a valid member of ...." error without wrapping the code in a ton of if statements?
Using Pcall for my game!
Best approach if datastore requests fail?
Ping pong event with InvokeClient breaking when localscript destroyed
My tools don't save sometimes
DataStore error breaking features (DataStore request was added to queue)
How to send `GetAsync` or `SetAsync` as an function argument?
Why does leaderstats not show up in my player? (player.leaderstats)
"pcall()" Coding Meaning
Check for a property
How can I check if a sound is moderated/deleted?
Issue - Attempt to index Nil with Name
Datastore/Leaderboard Problem
StarterGui:SetCore()
IntValues won't move to leaderstats(Stats)
Issue with .Touched script not always working?
Using a DataStore to store ownership status on Vehicles
How would i detect if roblox is slow/a service is down?
[BETA RELEASE] ModerationPlus: A easy ban function
Function giving an error
How to make a money gui that updates because of how long the player has been playing
Problem with player walkspeed
10:55:32.109 - Workspace.Script:4: attempt to index nil with number
How do i create a saving system for parts using DataStores?
Leaderstats script doesnt work
Trouble with leaderstats saving
Looping Function Producing Errors
It is possible make a Clan System?
Data Not Saving 2
Something wrong with DataStore
Scripting resource links
Sound is not loading
Which is faster pcall with dot operator or :FindFirstChild()
How to make a VIP multiplier?
Teleport fail but it used to work
Resources on Scripting help | Resource Compilation
Problems with insert model script

Oh, my work’s cut out for me. I don’t have to spend seven thousand days writing this one up now. I had a thread in planning but I can throw that out now.

One thing to add which I don’t believe was noted here: considering pcall is a callback, you can also return values to be used. If you feel uncomfortable with directly wrapping functions, you can create an anonymous function and return values.

Just try to stay away from creating excess upvalues unnecessarily.

local success, result = pcall(DataStore.GetAsync, DataStore, key)

-- Can become:

local success, result = pcall(function ()
    return DataStore:GetAsync(key)
end)
38 Likes

Worth noting:

  • xpcall doesn’t allow yielding. (roblox didn’t change it to allow yielding)
  • pcall creates a new thread
  • error message returned by pcall/xpcall does not contain a stack trace (although because pcall creates a new thread, you can retrieve stack info, and xpcall calls its error handler before unwinding the stack)

This is worded poorly, sugar syntax : can only be used only while calling the function.
a:b(...) is sugar for a.b(a,...), but you can’t do

local a = b:c

Instead, it should be described as calling (protected) GetAsync with the datastore, and the key

local success,response = pcall(
    ds.GetAsync, -- Get the GetAsync function to pcall
    ds, -- First argument (the datastore), this is the hidden self in a:b(...)
    "key" -- Second argument (the key)
)
14 Likes

Great tutorial for beginners,

Can you give is a FULL list of functions / things that we should always wrap in a pcall?

sometimes it’s not clear what function can fail, ie; using Setcore

And also an example of debug.traceback, The Wiki doesn’t explain well enough

7 Likes

@colbert2677 Yep, you can do that if you don’t want to use pcalls like I showed or if you want to do anything else inside the pcall.

Infact, you can create just one function and call it inside the pcall passing parameters to it by giving the pcall more arguments.
ie.

function get(key)
    print("Getting data with key:", key)
    return DataStore:GetAsync(key)
end

local success, response = pcall(get, "key")

@Halalaluyafail3 Well, I didn’t even know that xpcalls couldn’t yield - thanks for telling I added that to the thread.

Also thanks again for explaining the sugar syntax : better than me.


@RuizuKun_Dev I added a list of functions that I know that need to be pcall’d, if anyone has any to add make sure to reply.

There’s also a example of debug.traceback() now. :sun_with_face:

3 Likes

promises are a good alternative. here’s an excellent library and explanation: Promises and Why You Should Use Them

3 Likes

In the OP’s " DataStore errors - retrying" section, both sections of code have a variable called “data” that I’m assuming that should be the variable “response” instead?

2 Likes

Ah something from the previous code revision, thanks for bringing my attention to it.

5 Likes

Hey, sorry to bump the thread back up, I just thought it would be necessary to put this on here. Should most marketplace functions use pcalls, and which ones?

1 Like

I would use pcalls on the ones that return information.

2 Likes

@Vong25 is absolutely correct, any of those which return information fetch from the web APIs.

2 Likes

@ReturnedTrue can you look into this?

I also wonder if InsertService.LoadAsset can fail in normal circumstances

1 Like

We think alike. I’m certain it could potentially fail although I haven’t seen that occur.

LoadAsset can definitely fail, I’ve had on multiple occasions.

2 Likes