Error reference
Roblox DataStore not saving
Start by finding out which part failed. Did the game load the wrong value, lose a change before saving, or write a stale profile after a rejoin?
Start with a failed-load test
Force your test reader to fail. Then run the same save or leave path that would usually persist the profile. The expected result is zero writes from that session. Separately check that a successful read of an absent key can initialize a new profile, and that an existing profile keeps its data.
Run the free Luau example to see one unwanted write before the guard and zero after it. The example uses an in-memory store. Run it in a blank Studio place or with Lune.
1. Read and write errors need different handling
Wrap DataStore requests in a protected call and inspect the result. A failed read leaves the profile unknown. A failed write can leave its final state uncertain, so check the stored value and your retry strategy before repeating a non-idempotent change. Roblox error handling
-- a failed request kills the whole thread here
store:SetAsync(key, data)
-- a protected call lets you log and handle a failed request
local ok, err = pcall(function()
store:SetAsync(key, data)
end)
if not ok then
warn(("save failed for %s: %s"):format(key, tostring(err)))
end
2. Saving only on PlayerRemoving
Use your persistence layer's shutdown path to flush pending changes. Let leave and shutdown share the same per-profile save coordination so they cannot write competing snapshots. Roblox waits at most 30 seconds for functions registered with BindToClose, so keep retries bounded and preserve periodic saves. Roblox shutdown callback
3. API services are off, or the place is unpublished
For real DataStore tests, publish a separate test experience and use test accounts and data. In that experience, enable File → Experience Settings → Security → Enable Studio Access to API Services. Studio can access the same stores as the live experience, so keep Studio API access off on the live experience. A separate place inside the same experience shares its DataStores. Roblox Studio access guidance
4. SetAsync where you needed UpdateAsync
SetAsync replaces the stored value. When a change depends on the current value, use UpdateAsync and keep the transform free of yields and side effects because Roblox may call it again. Keep the session-ownership check inside the update where your design requires one. Roblox update guidance
-- race: reads, then writes something already stale
local coins = store:GetAsync(key)
store:SetAsync(key, coins + 100)
-- atomic: the transform sees the current value
store:UpdateAsync(key, function(old)
return (old or 0) + 100
end)
5. You are over the request budget
Queued requests can yield and eventually fail if the queue fills. Coalesce routine changes, stagger autosaves and check budget before a burst. UpdateAsync consumes both read and write budgets. A budget check is a scheduling signal. Keep error handling on the actual call. Roblox request limits
6. The value cannot be serialised
DataStores store JSON-shaped data. Instances, functions, Vector3, Color3 and CFrame are not valid — convert them to plain numbers first. Tables must use either all string keys or a clean array; a table with mixed keys, or holes in the middle of an array, does not survive the round trip intact.
7. No session locking, so a rejoin overwrites a live session
When two servers hold the same full profile, an older session can overwrite newer progress. Check how your persistence layer acquires ownership and rejects writes from a previous owner after a takeover. Test the late write explicitly. A timestamp by itself does not establish ownership. Roblox DataStore best practices
How to actually verify a save
- Change something, then rejoin. Printing “saved” proves the code ran, not that the write landed.
- Test with the server shutting down under you, not just with one player leaving.
- Test a fast rejoin across servers in the separate test experience. Two simulated clients in one Studio server can check client behavior, but do not exercise server ownership turnover.
- Watch Output for throttling warnings while doing it — a save that queues is a save that may not land before shutdown.
- Read the value back without cache and compare it with the expected value. Roblox cache guidance
Turn your save bug into one repeatable test
Tell us what the player did and what value they saw after rejoining. Baseplate can help you define the next check in writing. Start with a description of the symptom.
Get free written help