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!

402 Likes
What is pcall()?
My DataStore randomly failed?
Are There Any Good Tutorials On "PCalls"?
Scripting Resources Mega List
DataStore not working
Is a PCALL neccessary here? (ProfileService)
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
How can I check if a sound is moderated/deleted?
Coroutine not working
How can you check if a HTTP Request when using HttpService failed?
Issue with DataStores
How intensive is pcall()?
ModerationService - Simplyifying Moderation
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
TeleportService cannot cast string into int64
Error Finding Script
How do I receive the table from a RemoteEvent
How can I improve this datastore?
Can anyone explain what pcall is?
Attempt to call a nil value
Datastore/Leaderboard Problem
Data.key returns negative
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
(still need help) Cant find a work around for my health datastore
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 a DataStore to store ownership status on Vehicles
Using Pcall for my game!
Help with data store
Best approach if datastore requests fail?
Check for a property
Ping pong event with InvokeClient breaking when localscript destroyed
My tools don't save sometimes
Issue with .Touched script not always working?
DataStore error breaking features (DataStore request was added to queue)
How to send `GetAsync` or `SetAsync` as an function argument?
"pcall()" Coding Meaning
How would i detect if roblox is slow/a service is down?
Problem with player walkspeed
Function giving an error
[BETA RELEASE] ModerationPlus: A easy ban function
How to get player's current t-shirt
IntValues won't move to leaderstats(Stats)
Ignore HTTP 404 Error
[SOLVED] How to get asset id of a group icon?
StarterGui:SetCore()
Why my datastore doesn't work
Confusion about looping datastores to retry failures
Does this leaderstats code actually save the stats, or no?
Issue - Attempt to index Nil with Name
Saving Bool or Int Value
What is a pcall() function and how do i use it and when should i use it?
Unable to cast to Array when using :SetAsync()
Detecting "SendNotification"
How do I check if someone has already joined my game before?
Why does leaderstats not show up in my player? (player.leaderstats)
DataStore request was added to queue. If request queue fills, further requests will be dropped. Try sending fewer requests
UserHasBadgeAsync In Chat Tags
How can I fix this script?
How should I remove a player's data from datastores correctly?
How can I fix this camera bug?
Do you use pcalls?
Delete this Delete this Delete this
Struggling with scripting
UserHasBadgeAsync: Parsed invalid JSON: "Too many requests"
Pcall(), xpcall() and ypcall() Tutorial - How do they work?
My script have a problem. Punch card
How to properly use pcall()
Datastore headache
Need help resolving this AFK system
HTTP 429 (Too many requests) Chat filtering
Datastore does not save and there is no errors
Overwrite Roblox's Output functions?
How To Use, Utilize, and Succeed with DataStores
RemoteFunction is not working "InvokeServer()"
Data storage saving other values except 2
How to detect if GetUserIdFromNameAsync() is null
10:55:32.109 - Workspace.Script:4: attempt to index nil with number
Error trying to make Datastore
Is it ok to not use pcall in important scripts?
Redemption Codes System
I cant seem to get datastoring a string to work
What does pcall do?
Help with teleportservice
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
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
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?
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)

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)
56 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)
)
18 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

10 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:

4 Likes

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

4 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.

4 Likes

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

3 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

Can I have two functions in a pcall?

pcall(function()

pointsStore:SetAsync()
coinsStore:SetAsync()

end)

yes, but if pointStore:SetAsync() fails then coinsStore:SetAsync() will not be run either, it can only catch one error per pcall, and can call many functions

This has been a very useful resource for my programming on roblox. Thank you for making it.

5 Likes

Are there any other usages for pcall besides using it on functions for return information fetch from the web APIs? Are pcalls only used for those functions? Thanks

1 Like

You can use pcalls for anything, especially when there is a chance of an error being thrown. I personally use pcalls for my own functions to return statuses and results.

For every function that returns values? Instead of using pcalls, can you simply check if returnedvalue ~= nil then end for functions that don’t correlate with API?

You can simply check that; however, some functions will emit errors, stopping the script, so pcalls can help prevent the script from stopping entirely.