Lua Error Recovery
Error recovery is what a program does after a failure so the job can still finish. It retries a temporary failure, uses a safe value when retries run out, or restarts from the last good state.
Error objects tell retryable failures from fatal ones
Two guarded calls return two kinds, and the code reads each kind.
local function fail(kind, message) error({kind = kind, message = message}, 0) end
local ok, err = pcall(fail, "retryable", "busy")
print(ok)
print(err.kind)
print(err.message)
local ok2, err2 = pcall(fail, "fatal", "missing")
print(err2.kind)Retries run out, then the fallback value stands
Three tries fail, and the safe value stays in place.
local tries = 0
local function fetch()
tries = tries + 1
error({kind = "retryable", message = "busy"}, 0)
end
local value = "cached"
for attempt = 1, 3 do
local ok, result = pcall(fetch)
if ok then value = result break end
end
print(value)
print(tries)A job resumes from its last good place
The failed item is tried again, and no finished item repeats.
local items = {"one", "two", "three"}
local busy = true
local done = {}
local at = 1
while at <= #items do
local item = items[at]
local ok, result = pcall(function()
if busy and item == "two" then error({kind = "retryable", message = "busy"}, 0) end
return item
end)
if ok then done[#done + 1] = result at = at + 1 else busy = false end
end
print(table.concat(done, ","))Example
local tries = 0
local function fetch()
tries = tries + 1
if tries < 3 then error({kind = "retryable", message = "busy"}, 0) end
return "data"
end
local value = "cached"
for attempt = 1, 3 do
local ok, result = pcall(fetch)
if ok then value = result break end
end
print(value)
print(tries)
local items = {"one", "two"}
local busy = true
local done = {}
local at = 1
while at <= #items do
local item = items[at]
local ok, result = pcall(function()
if busy and item == "two" then error({kind = "retryable", message = "busy"}, 0) end
return item
end)
if ok then done[#done + 1] = result at = at + 1 else busy = false end
end
print(table.concat(done, ","))
local ok, err = pcall(function() error({kind = "fatal", message = "missing"}, 0) end)
local outcome = "kept"
if not ok and err.kind == "fatal" then outcome = "stopped" end
print(outcome)Key points
- Retryable error: a temporary failure, like a busy resource, that may succeed on a later try.
- Fatal error: a permanent failure, like a missing value, that retrying cannot fix.
- Error object: a table carrying a kind and a message, so code can tell failures apart.
- Fallback value: a safe value used when the real value cannot be produced.
- Resume state: the last good result kept aside, so a job restarts from there instead of from the start.
- Retry limit: a fixed number of tries, so a retryable failure cannot loop forever.
In this example, the fetch fails twice and succeeds on the third try, so the value is data. The job then hits a busy item, keeps its place, and finishes without repeating an item. The fatal error is told apart by its kind, so the job stops instead of retrying.
Note
pcall returns the error object itself when a function raises a table, so the kind survives the guarded call.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
An error object has kind fatal. What should recovery do?