[{"data":1,"prerenderedAt":4539},["ShallowReactive",2],{"page-\u002Flua\u002F09-error-handling":3},{"id":4,"title":5,"body":6,"description":4530,"extension":4531,"meta":4532,"navigation":113,"path":4533,"seo":4534,"stem":4537,"__hash__":4538},"content\u002Flua\u002F09-error-handling.md","Lua 09 — Error Handling, pcall & Structured Errors",{"type":7,"value":8,"toc":4522},"minimark",[9,19,50,57,3930,3943,3968,3977,4050,4054,4066,4119,4131,4141,4160,4238,4242,4518],[10,11,13,14,18],"h1",{"id":12},"_09-error-handling-pcall-structured-errors","09 — Error Handling, ",[15,16,17],"code",{},"pcall"," & Structured Errors",[20,21,22,23,26,27,30,31,34,35,30,38,41,42,45,46,49],"p",{},"Lua's error model: ",[15,24,25],{},"error()"," throws, ",[15,28,29],{},"pcall()","\u002F",[15,32,33],{},"xpcall()"," catch. There are no ",[15,36,37],{},"try",[15,39,40],{},"catch"," keywords — error handling is functional. Errors carry a message (string or any value). The ",[15,43,44],{},"debug"," library provides stack traces and local variable introspection. Lua 5.4 adds ",[15,47,48],{},"\u003Cclose>"," for deterministic cleanup. This chapter covers production patterns: structured errors, error propagation with level correction, retry\u002Fcircuit-breaker logic, and traceback-preserving error handlers.",[51,52,54,56],"h2",{"id":53},"error-throwing",[15,55,25],{}," — Throwing",[58,59,61,291,297],"code-wrapper",{"language":60},"lua",[62,63,67],"pre",{"className":64,"code":65,"language":60,"meta":66,"style":66},"language-lua shiki shiki-themes github-light github-dark","-- error(msg, level?): throws an error\n-- msg: string (converted with tostring) OR any value (table, number)\n-- level: controls which line the error points to\n--   1 (default): the error() call itself\n--   2: the caller of the function containing error() (useful for library functions)\n--   0: no position info added\n\nlocal function divide(a, b)\n  if b == 0 then\n    error(\"division by zero\", 2)  -- level 2: error points to divide() CALLER, not this line\n  end\n  return a \u002F b\nend\n\n-- Without level 2, the error points to line 4 (inside divide) — misleading\n-- With level 2, the error points to the line that CALLED divide(10, 0)\ndivide(10, 0)  -- error: stdin:LINE: division by zero (points here, not inside divide)\n\n-- Error with a table (structured error — Lua 5.2+ preserves the table)\nerror({code = \"E_AUTH\", message = \"invalid token\", details = {user = \"alice\"}})\n","",[15,68,69,78,84,90,96,102,108,115,133,152,177,183,197,203,208,214,220,241,246,252],{"__ignoreMap":66},[70,71,74],"span",{"class":72,"line":73},"line",1,[70,75,77],{"class":76},"sdCPZ","-- error(msg, level?): throws an error\n",[70,79,81],{"class":72,"line":80},2,[70,82,83],{"class":76},"-- msg: string (converted with tostring) OR any value (table, number)\n",[70,85,87],{"class":72,"line":86},3,[70,88,89],{"class":76},"-- level: controls which line the error points to\n",[70,91,93],{"class":72,"line":92},4,[70,94,95],{"class":76},"--   1 (default): the error() call itself\n",[70,97,99],{"class":72,"line":98},5,[70,100,101],{"class":76},"--   2: the caller of the function containing error() (useful for library functions)\n",[70,103,105],{"class":72,"line":104},6,[70,106,107],{"class":76},"--   0: no position info added\n",[70,109,111],{"class":72,"line":110},7,[70,112,114],{"emptyLinePlaceholder":113},true,"\n",[70,116,118,122,125,129],{"class":72,"line":117},8,[70,119,121],{"class":120},"svdQ7","local",[70,123,124],{"class":120}," function",[70,126,128],{"class":127},"sIsaT"," divide",[70,130,132],{"class":131},"ssxIu","(a, b)\n",[70,134,136,139,142,145,149],{"class":72,"line":135},9,[70,137,138],{"class":120},"  if",[70,140,141],{"class":131}," b ",[70,143,144],{"class":120},"==",[70,146,148],{"class":147},"snvgF"," 0",[70,150,151],{"class":120}," then\n",[70,153,155,158,161,165,168,171,174],{"class":72,"line":154},10,[70,156,157],{"class":147},"    error",[70,159,160],{"class":131},"(",[70,162,164],{"class":163},"sJ6F3","\"division by zero\"",[70,166,167],{"class":131},", ",[70,169,170],{"class":147},"2",[70,172,173],{"class":131},")  ",[70,175,176],{"class":76},"-- level 2: error points to divide() CALLER, not this line\n",[70,178,180],{"class":72,"line":179},11,[70,181,182],{"class":120},"  end\n",[70,184,186,189,192,194],{"class":72,"line":185},12,[70,187,188],{"class":120},"  return",[70,190,191],{"class":131}," a ",[70,193,30],{"class":120},[70,195,196],{"class":131}," b\n",[70,198,200],{"class":72,"line":199},13,[70,201,202],{"class":120},"end\n",[70,204,206],{"class":72,"line":205},14,[70,207,114],{"emptyLinePlaceholder":113},[70,209,211],{"class":72,"line":210},15,[70,212,213],{"class":76},"-- Without level 2, the error points to line 4 (inside divide) — misleading\n",[70,215,217],{"class":72,"line":216},16,[70,218,219],{"class":76},"-- With level 2, the error points to the line that CALLED divide(10, 0)\n",[70,221,223,226,228,231,233,236,238],{"class":72,"line":222},17,[70,224,225],{"class":147},"divide",[70,227,160],{"class":131},[70,229,230],{"class":147},"10",[70,232,167],{"class":131},[70,234,235],{"class":147},"0",[70,237,173],{"class":131},[70,239,240],{"class":76},"-- error: stdin:LINE: division by zero (points here, not inside divide)\n",[70,242,244],{"class":72,"line":243},18,[70,245,114],{"emptyLinePlaceholder":113},[70,247,249],{"class":72,"line":248},19,[70,250,251],{"class":76},"-- Error with a table (structured error — Lua 5.2+ preserves the table)\n",[70,253,255,258,261,264,267,270,272,275,278,280,283,285,288],{"class":72,"line":254},20,[70,256,257],{"class":147},"error",[70,259,260],{"class":131},"({code ",[70,262,263],{"class":120},"=",[70,265,266],{"class":163}," \"E_AUTH\"",[70,268,269],{"class":131},", message ",[70,271,263],{"class":120},[70,273,274],{"class":163}," \"invalid token\"",[70,276,277],{"class":131},", details ",[70,279,263],{"class":120},[70,281,282],{"class":131}," {user ",[70,284,263],{"class":120},[70,286,287],{"class":163}," \"alice\"",[70,289,290],{"class":131},"}})\n",[51,292,294,296],{"id":293},"pcall-protected-call",[15,295,29],{}," — Protected Call",[58,298,299,571,577],{"language":60},[62,300,302],{"className":64,"code":301,"language":60,"meta":66,"style":66},"-- pcall(f, args...): calls f in protected mode\n-- Returns: true, result1, result2, ... on success (all return values)\n-- Returns: false, error_message on failure (only 2 values)\n\nlocal ok, result = pcall(function(a, b)\n  return a \u002F b\nend, 10, 2)\nprint(ok, result)  -- true  5\n\nlocal ok2, err = pcall(function()\n  error(\"boom\")\nend)\nprint(ok2, err)  -- false  \"stdin:LINE: boom\" (error adds source:line prefix to string errors)\n\n-- pcall with multiple return values:\nlocal function minmax(a, b) return a \u003C b and a or b, a \u003C b and b or a end\nlocal ok3, lo, hi = pcall(minmax, 10, 5)\nprint(ok3, lo, hi)  -- true  5  10\n\n-- On error: only 2 return values (ok, err) — extra values are nil\nlocal ok4, e1, e2 = pcall(function() error(\"oops\") end)\nprint(ok4, e1, e2)  -- false  \"oops\"  nil (only ok + err, no 3rd value)\n",[15,303,304,309,314,319,323,342,352,368,379,383,401,413,419,429,433,438,485,508,518,522,527,560],{"__ignoreMap":66},[70,305,306],{"class":72,"line":73},[70,307,308],{"class":76},"-- pcall(f, args...): calls f in protected mode\n",[70,310,311],{"class":72,"line":80},[70,312,313],{"class":76},"-- Returns: true, result1, result2, ... on success (all return values)\n",[70,315,316],{"class":72,"line":86},[70,317,318],{"class":76},"-- Returns: false, error_message on failure (only 2 values)\n",[70,320,321],{"class":72,"line":92},[70,322,114],{"emptyLinePlaceholder":113},[70,324,325,327,330,332,335,337,340],{"class":72,"line":98},[70,326,121],{"class":120},[70,328,329],{"class":131}," ok, result ",[70,331,263],{"class":120},[70,333,334],{"class":147}," pcall",[70,336,160],{"class":131},[70,338,339],{"class":120},"function",[70,341,132],{"class":131},[70,343,344,346,348,350],{"class":72,"line":104},[70,345,188],{"class":120},[70,347,191],{"class":131},[70,349,30],{"class":120},[70,351,196],{"class":131},[70,353,354,357,359,361,363,365],{"class":72,"line":110},[70,355,356],{"class":120},"end",[70,358,167],{"class":131},[70,360,230],{"class":147},[70,362,167],{"class":131},[70,364,170],{"class":147},[70,366,367],{"class":131},")\n",[70,369,370,373,376],{"class":72,"line":117},[70,371,372],{"class":147},"print",[70,374,375],{"class":131},"(ok, result)  ",[70,377,378],{"class":76},"-- true  5\n",[70,380,381],{"class":72,"line":135},[70,382,114],{"emptyLinePlaceholder":113},[70,384,385,387,390,392,394,396,398],{"class":72,"line":154},[70,386,121],{"class":120},[70,388,389],{"class":131}," ok2, err ",[70,391,263],{"class":120},[70,393,334],{"class":147},[70,395,160],{"class":131},[70,397,339],{"class":120},[70,399,400],{"class":131},"()\n",[70,402,403,406,408,411],{"class":72,"line":179},[70,404,405],{"class":147},"  error",[70,407,160],{"class":131},[70,409,410],{"class":163},"\"boom\"",[70,412,367],{"class":131},[70,414,415,417],{"class":72,"line":185},[70,416,356],{"class":120},[70,418,367],{"class":131},[70,420,421,423,426],{"class":72,"line":199},[70,422,372],{"class":147},[70,424,425],{"class":131},"(ok2, err)  ",[70,427,428],{"class":76},"-- false  \"stdin:LINE: boom\" (error adds source:line prefix to string errors)\n",[70,430,431],{"class":72,"line":205},[70,432,114],{"emptyLinePlaceholder":113},[70,434,435],{"class":72,"line":210},[70,436,437],{"class":76},"-- pcall with multiple return values:\n",[70,439,440,442,444,447,450,453,455,458,460,463,465,468,471,473,475,477,479,481,483],{"class":72,"line":216},[70,441,121],{"class":120},[70,443,124],{"class":120},[70,445,446],{"class":127}," minmax",[70,448,449],{"class":131},"(a, b) ",[70,451,452],{"class":120},"return",[70,454,191],{"class":131},[70,456,457],{"class":120},"\u003C",[70,459,141],{"class":131},[70,461,462],{"class":120},"and",[70,464,191],{"class":131},[70,466,467],{"class":120},"or",[70,469,470],{"class":131}," b, a ",[70,472,457],{"class":120},[70,474,141],{"class":131},[70,476,462],{"class":120},[70,478,141],{"class":131},[70,480,467],{"class":120},[70,482,191],{"class":131},[70,484,202],{"class":120},[70,486,487,489,492,494,496,499,501,503,506],{"class":72,"line":222},[70,488,121],{"class":120},[70,490,491],{"class":131}," ok3, lo, hi ",[70,493,263],{"class":120},[70,495,334],{"class":147},[70,497,498],{"class":131},"(minmax, ",[70,500,230],{"class":147},[70,502,167],{"class":131},[70,504,505],{"class":147},"5",[70,507,367],{"class":131},[70,509,510,512,515],{"class":72,"line":243},[70,511,372],{"class":147},[70,513,514],{"class":131},"(ok3, lo, hi)  ",[70,516,517],{"class":76},"-- true  5  10\n",[70,519,520],{"class":72,"line":248},[70,521,114],{"emptyLinePlaceholder":113},[70,523,524],{"class":72,"line":254},[70,525,526],{"class":76},"-- On error: only 2 return values (ok, err) — extra values are nil\n",[70,528,530,532,535,537,539,541,543,546,548,550,553,556,558],{"class":72,"line":529},21,[70,531,121],{"class":120},[70,533,534],{"class":131}," ok4, e1, e2 ",[70,536,263],{"class":120},[70,538,334],{"class":147},[70,540,160],{"class":131},[70,542,339],{"class":120},[70,544,545],{"class":131},"() ",[70,547,257],{"class":147},[70,549,160],{"class":131},[70,551,552],{"class":163},"\"oops\"",[70,554,555],{"class":131},") ",[70,557,356],{"class":120},[70,559,367],{"class":131},[70,561,563,565,568],{"class":72,"line":562},22,[70,564,372],{"class":147},[70,566,567],{"class":131},"(ok4, e1, e2)  ",[70,569,570],{"class":76},"-- false  \"oops\"  nil (only ok + err, no 3rd value)\n",[51,572,574,576],{"id":573},"xpcall-protected-call-with-handler",[15,575,33],{}," — Protected Call with Handler",[58,578,579,816,820],{"language":60},[62,580,582],{"className":64,"code":581,"language":60,"meta":66,"style":66},"-- xpcall(f, handler, args...): like pcall, but handler runs before the stack unwinds\n-- The handler receives the error message and can capture a traceback\n-- CRITICAL: debug.traceback() must be called in the handler, not later —\n-- the stack is unwound by the time pcall returns.\n\nlocal function with_traceback(fn)\n  return xpcall(fn, function(err)\n    -- err is the raw error value (string, table, or anything thrown by error())\n    return {\n      message = tostring(err),\n      traceback = debug.traceback(tostring(err), 2),  -- capture NOW, before unwind\n      timestamp = os.time(),\n    }\n  end)\nend\n\nlocal ok, err_obj = with_traceback(function()\n  error({code = \"E_FAIL\", msg = \"something broke\"})\nend)\nif not ok then\n  print(err_obj.message)     -- \"table: 0x...\" (tostring on a table)\n  print(err_obj.traceback)   -- full stack trace\nend\n",[15,583,584,589,594,599,604,608,620,635,640,648,661,687,700,705,712,716,720,737,759,765,779,796,811],{"__ignoreMap":66},[70,585,586],{"class":72,"line":73},[70,587,588],{"class":76},"-- xpcall(f, handler, args...): like pcall, but handler runs before the stack unwinds\n",[70,590,591],{"class":72,"line":80},[70,592,593],{"class":76},"-- The handler receives the error message and can capture a traceback\n",[70,595,596],{"class":72,"line":86},[70,597,598],{"class":76},"-- CRITICAL: debug.traceback() must be called in the handler, not later —\n",[70,600,601],{"class":72,"line":92},[70,602,603],{"class":76},"-- the stack is unwound by the time pcall returns.\n",[70,605,606],{"class":72,"line":98},[70,607,114],{"emptyLinePlaceholder":113},[70,609,610,612,614,617],{"class":72,"line":104},[70,611,121],{"class":120},[70,613,124],{"class":120},[70,615,616],{"class":127}," with_traceback",[70,618,619],{"class":131},"(fn)\n",[70,621,622,624,627,630,632],{"class":72,"line":110},[70,623,188],{"class":120},[70,625,626],{"class":147}," xpcall",[70,628,629],{"class":131},"(fn, ",[70,631,339],{"class":120},[70,633,634],{"class":131},"(err)\n",[70,636,637],{"class":72,"line":117},[70,638,639],{"class":76},"    -- err is the raw error value (string, table, or anything thrown by error())\n",[70,641,642,645],{"class":72,"line":135},[70,643,644],{"class":120},"    return",[70,646,647],{"class":131}," {\n",[70,649,650,653,655,658],{"class":72,"line":154},[70,651,652],{"class":131},"      message ",[70,654,263],{"class":120},[70,656,657],{"class":147}," tostring",[70,659,660],{"class":131},"(err),\n",[70,662,663,666,668,671,673,676,679,681,684],{"class":72,"line":179},[70,664,665],{"class":131},"      traceback ",[70,667,263],{"class":120},[70,669,670],{"class":147}," debug.traceback",[70,672,160],{"class":131},[70,674,675],{"class":147},"tostring",[70,677,678],{"class":131},"(err), ",[70,680,170],{"class":147},[70,682,683],{"class":131},"),  ",[70,685,686],{"class":76},"-- capture NOW, before unwind\n",[70,688,689,692,694,697],{"class":72,"line":185},[70,690,691],{"class":131},"      timestamp ",[70,693,263],{"class":120},[70,695,696],{"class":147}," os.time",[70,698,699],{"class":131},"(),\n",[70,701,702],{"class":72,"line":199},[70,703,704],{"class":131},"    }\n",[70,706,707,710],{"class":72,"line":205},[70,708,709],{"class":120},"  end",[70,711,367],{"class":131},[70,713,714],{"class":72,"line":210},[70,715,202],{"class":120},[70,717,718],{"class":72,"line":216},[70,719,114],{"emptyLinePlaceholder":113},[70,721,722,724,727,729,731,733,735],{"class":72,"line":222},[70,723,121],{"class":120},[70,725,726],{"class":131}," ok, err_obj ",[70,728,263],{"class":120},[70,730,616],{"class":147},[70,732,160],{"class":131},[70,734,339],{"class":120},[70,736,400],{"class":131},[70,738,739,741,743,745,748,751,753,756],{"class":72,"line":243},[70,740,405],{"class":147},[70,742,260],{"class":131},[70,744,263],{"class":120},[70,746,747],{"class":163}," \"E_FAIL\"",[70,749,750],{"class":131},", msg ",[70,752,263],{"class":120},[70,754,755],{"class":163}," \"something broke\"",[70,757,758],{"class":131},"})\n",[70,760,761,763],{"class":72,"line":248},[70,762,356],{"class":120},[70,764,367],{"class":131},[70,766,767,770,773,776],{"class":72,"line":254},[70,768,769],{"class":120},"if",[70,771,772],{"class":120}," not",[70,774,775],{"class":131}," ok ",[70,777,778],{"class":120},"then\n",[70,780,781,784,787,790,793],{"class":72,"line":529},[70,782,783],{"class":147},"  print",[70,785,786],{"class":131},"(err_obj.",[70,788,789],{"class":127},"message",[70,791,792],{"class":131},")     ",[70,794,795],{"class":76},"-- \"table: 0x...\" (tostring on a table)\n",[70,797,798,800,802,805,808],{"class":72,"line":562},[70,799,783],{"class":147},[70,801,786],{"class":131},[70,803,804],{"class":127},"traceback",[70,806,807],{"class":131},")   ",[70,809,810],{"class":76},"-- full stack trace\n",[70,812,814],{"class":72,"line":813},23,[70,815,202],{"class":120},[51,817,819],{"id":818},"structured-errors-with-metatables","Structured Errors with Metatables",[58,821,822,1493,1504],{"language":60},[62,823,825],{"className":64,"code":824,"language":60,"meta":66,"style":66},"-- Production: typed error objects with error code, category, context\nlocal Error = {}\nError.__index = Error\nError.__tostring = function(self)\n  return string.format(\"[%s] %s: %s\", self.category, self.code, self.message)\nend\n\nfunction Error.new(opts)\n  return setmetatable({\n    code = opts.code or \"UNKNOWN\",\n    message = opts.message or \"\",\n    category = opts.category or \"GENERIC\",\n    context = opts.context or {},\n    cause = opts.cause,  -- chain: original error if wrapping\n  }, Error)\nend\n\n-- Error constructors by category\nlocal function err_validation(msg, context)\n  return Error.new({code = \"VALIDATION\", message = msg, category = \"CLIENT\", context = context})\nend\nlocal function err_not_found(resource, id)\n  return Error.new({code = \"NOT_FOUND\", message = resource .. \" not found: \" .. tostring(id), category = \"CLIENT\"})\nend\nlocal function err_internal(msg, context)\n  return Error.new({code = \"INTERNAL\", message = msg, category = \"SERVER\", context = context})\nend\n\n-- Usage: throw structured errors\nlocal function get_user(id)\n  if type(id) ~= \"number\" then\n    error(err_validation(\"user id must be number\", {provided = id}), 2)\n  end\n  if id \u003C 0 then\n    error(err_not_found(\"user\", id), 2)\n  end\n  -- ... fetch user\n  return {id = id, name = \"Alice\"}\nend\n\n-- Catch and inspect\nlocal ok, result = pcall(get_user, \"abc\")\nif not ok then\n  if getmetatable(result) == Error then\n    print(tostring(result))      -- [CLIENT] VALIDATION: user id must be number\n    print(result.context)        -- {provided = \"abc\"}\n  else\n    -- unexpected error type (string, etc.)\n    error(\"unexpected error: \" .. tostring(result))\n  end\nend\n",[15,826,827,832,844,858,872,913,917,921,936,946,967,985,1003,1020,1038,1043,1047,1051,1056,1068,1104,1108,1120,1161,1166,1178,1211,1216,1221,1227,1240,1259,1286,1291,1305,1327,1332,1338,1359,1364,1369,1375,1394,1405,1422,1438,1454,1460,1466,1483,1488],{"__ignoreMap":66},[70,828,829],{"class":72,"line":73},[70,830,831],{"class":76},"-- Production: typed error objects with error code, category, context\n",[70,833,834,836,839,841],{"class":72,"line":80},[70,835,121],{"class":120},[70,837,838],{"class":131}," Error ",[70,840,263],{"class":120},[70,842,843],{"class":131}," {}\n",[70,845,846,849,852,855],{"class":72,"line":86},[70,847,848],{"class":131},"Error.",[70,850,851],{"class":127},"__index",[70,853,854],{"class":120}," =",[70,856,857],{"class":131}," Error\n",[70,859,860,862,865,867,869],{"class":72,"line":92},[70,861,848],{"class":131},[70,863,864],{"class":127},"__tostring",[70,866,854],{"class":120},[70,868,124],{"class":120},[70,870,871],{"class":131},"(self)\n",[70,873,874,876,879,881,884,886,889,892,895,897,899,901,903,905,907,909,911],{"class":72,"line":98},[70,875,188],{"class":120},[70,877,878],{"class":147}," string.format",[70,880,160],{"class":131},[70,882,883],{"class":163},"\"[%s] %s: %s\"",[70,885,167],{"class":131},[70,887,888],{"class":147},"self",[70,890,891],{"class":131},".",[70,893,894],{"class":127},"category",[70,896,167],{"class":131},[70,898,888],{"class":147},[70,900,891],{"class":131},[70,902,15],{"class":127},[70,904,167],{"class":131},[70,906,888],{"class":147},[70,908,891],{"class":131},[70,910,789],{"class":127},[70,912,367],{"class":131},[70,914,915],{"class":72,"line":104},[70,916,202],{"class":120},[70,918,919],{"class":72,"line":110},[70,920,114],{"emptyLinePlaceholder":113},[70,922,923,925,928,930,933],{"class":72,"line":117},[70,924,339],{"class":120},[70,926,927],{"class":127}," Error",[70,929,891],{"class":131},[70,931,932],{"class":127},"new",[70,934,935],{"class":131},"(opts)\n",[70,937,938,940,943],{"class":72,"line":135},[70,939,188],{"class":120},[70,941,942],{"class":147}," setmetatable",[70,944,945],{"class":131},"({\n",[70,947,948,951,953,956,958,961,964],{"class":72,"line":154},[70,949,950],{"class":131},"    code ",[70,952,263],{"class":120},[70,954,955],{"class":131}," opts.",[70,957,15],{"class":127},[70,959,960],{"class":120}," or",[70,962,963],{"class":163}," \"UNKNOWN\"",[70,965,966],{"class":131},",\n",[70,968,969,972,974,976,978,980,983],{"class":72,"line":179},[70,970,971],{"class":131},"    message ",[70,973,263],{"class":120},[70,975,955],{"class":131},[70,977,789],{"class":127},[70,979,960],{"class":120},[70,981,982],{"class":163}," \"\"",[70,984,966],{"class":131},[70,986,987,990,992,994,996,998,1001],{"class":72,"line":185},[70,988,989],{"class":131},"    category ",[70,991,263],{"class":120},[70,993,955],{"class":131},[70,995,894],{"class":127},[70,997,960],{"class":120},[70,999,1000],{"class":163}," \"GENERIC\"",[70,1002,966],{"class":131},[70,1004,1005,1008,1010,1012,1015,1017],{"class":72,"line":199},[70,1006,1007],{"class":131},"    context ",[70,1009,263],{"class":120},[70,1011,955],{"class":131},[70,1013,1014],{"class":127},"context",[70,1016,960],{"class":120},[70,1018,1019],{"class":131}," {},\n",[70,1021,1022,1025,1027,1029,1032,1035],{"class":72,"line":205},[70,1023,1024],{"class":131},"    cause ",[70,1026,263],{"class":120},[70,1028,955],{"class":131},[70,1030,1031],{"class":127},"cause",[70,1033,1034],{"class":131},",  ",[70,1036,1037],{"class":76},"-- chain: original error if wrapping\n",[70,1039,1040],{"class":72,"line":210},[70,1041,1042],{"class":131},"  }, Error)\n",[70,1044,1045],{"class":72,"line":216},[70,1046,202],{"class":120},[70,1048,1049],{"class":72,"line":222},[70,1050,114],{"emptyLinePlaceholder":113},[70,1052,1053],{"class":72,"line":243},[70,1054,1055],{"class":76},"-- Error constructors by category\n",[70,1057,1058,1060,1062,1065],{"class":72,"line":248},[70,1059,121],{"class":120},[70,1061,124],{"class":120},[70,1063,1064],{"class":127}," err_validation",[70,1066,1067],{"class":131},"(msg, context)\n",[70,1069,1070,1072,1075,1077,1079,1081,1084,1086,1088,1091,1093,1096,1099,1101],{"class":72,"line":254},[70,1071,188],{"class":120},[70,1073,1074],{"class":131}," Error.",[70,1076,932],{"class":147},[70,1078,260],{"class":131},[70,1080,263],{"class":120},[70,1082,1083],{"class":163}," \"VALIDATION\"",[70,1085,269],{"class":131},[70,1087,263],{"class":120},[70,1089,1090],{"class":131}," msg, category ",[70,1092,263],{"class":120},[70,1094,1095],{"class":163}," \"CLIENT\"",[70,1097,1098],{"class":131},", context ",[70,1100,263],{"class":120},[70,1102,1103],{"class":131}," context})\n",[70,1105,1106],{"class":72,"line":529},[70,1107,202],{"class":120},[70,1109,1110,1112,1114,1117],{"class":72,"line":562},[70,1111,121],{"class":120},[70,1113,124],{"class":120},[70,1115,1116],{"class":127}," err_not_found",[70,1118,1119],{"class":131},"(resource, id)\n",[70,1121,1122,1124,1126,1128,1130,1132,1135,1137,1139,1142,1145,1148,1150,1152,1155,1157,1159],{"class":72,"line":813},[70,1123,188],{"class":120},[70,1125,1074],{"class":131},[70,1127,932],{"class":147},[70,1129,260],{"class":131},[70,1131,263],{"class":120},[70,1133,1134],{"class":163}," \"NOT_FOUND\"",[70,1136,269],{"class":131},[70,1138,263],{"class":120},[70,1140,1141],{"class":131}," resource ",[70,1143,1144],{"class":120},"..",[70,1146,1147],{"class":163}," \" not found: \" ",[70,1149,1144],{"class":120},[70,1151,657],{"class":147},[70,1153,1154],{"class":131},"(id), category ",[70,1156,263],{"class":120},[70,1158,1095],{"class":163},[70,1160,758],{"class":131},[70,1162,1164],{"class":72,"line":1163},24,[70,1165,202],{"class":120},[70,1167,1169,1171,1173,1176],{"class":72,"line":1168},25,[70,1170,121],{"class":120},[70,1172,124],{"class":120},[70,1174,1175],{"class":127}," err_internal",[70,1177,1067],{"class":131},[70,1179,1181,1183,1185,1187,1189,1191,1194,1196,1198,1200,1202,1205,1207,1209],{"class":72,"line":1180},26,[70,1182,188],{"class":120},[70,1184,1074],{"class":131},[70,1186,932],{"class":147},[70,1188,260],{"class":131},[70,1190,263],{"class":120},[70,1192,1193],{"class":163}," \"INTERNAL\"",[70,1195,269],{"class":131},[70,1197,263],{"class":120},[70,1199,1090],{"class":131},[70,1201,263],{"class":120},[70,1203,1204],{"class":163}," \"SERVER\"",[70,1206,1098],{"class":131},[70,1208,263],{"class":120},[70,1210,1103],{"class":131},[70,1212,1214],{"class":72,"line":1213},27,[70,1215,202],{"class":120},[70,1217,1219],{"class":72,"line":1218},28,[70,1220,114],{"emptyLinePlaceholder":113},[70,1222,1224],{"class":72,"line":1223},29,[70,1225,1226],{"class":76},"-- Usage: throw structured errors\n",[70,1228,1230,1232,1234,1237],{"class":72,"line":1229},30,[70,1231,121],{"class":120},[70,1233,124],{"class":120},[70,1235,1236],{"class":127}," get_user",[70,1238,1239],{"class":131},"(id)\n",[70,1241,1243,1245,1248,1251,1254,1257],{"class":72,"line":1242},31,[70,1244,138],{"class":120},[70,1246,1247],{"class":147}," type",[70,1249,1250],{"class":131},"(id) ",[70,1252,1253],{"class":120},"~=",[70,1255,1256],{"class":163}," \"number\" ",[70,1258,778],{"class":120},[70,1260,1262,1264,1266,1269,1271,1274,1277,1279,1282,1284],{"class":72,"line":1261},32,[70,1263,157],{"class":147},[70,1265,160],{"class":131},[70,1267,1268],{"class":147},"err_validation",[70,1270,160],{"class":131},[70,1272,1273],{"class":163},"\"user id must be number\"",[70,1275,1276],{"class":131},", {provided ",[70,1278,263],{"class":120},[70,1280,1281],{"class":131}," id}), ",[70,1283,170],{"class":147},[70,1285,367],{"class":131},[70,1287,1289],{"class":72,"line":1288},33,[70,1290,182],{"class":120},[70,1292,1294,1296,1299,1301,1303],{"class":72,"line":1293},34,[70,1295,138],{"class":120},[70,1297,1298],{"class":131}," id ",[70,1300,457],{"class":120},[70,1302,148],{"class":147},[70,1304,151],{"class":120},[70,1306,1308,1310,1312,1315,1317,1320,1323,1325],{"class":72,"line":1307},35,[70,1309,157],{"class":147},[70,1311,160],{"class":131},[70,1313,1314],{"class":147},"err_not_found",[70,1316,160],{"class":131},[70,1318,1319],{"class":163},"\"user\"",[70,1321,1322],{"class":131},", id), ",[70,1324,170],{"class":147},[70,1326,367],{"class":131},[70,1328,1330],{"class":72,"line":1329},36,[70,1331,182],{"class":120},[70,1333,1335],{"class":72,"line":1334},37,[70,1336,1337],{"class":76},"  -- ... fetch user\n",[70,1339,1341,1343,1346,1348,1351,1353,1356],{"class":72,"line":1340},38,[70,1342,188],{"class":120},[70,1344,1345],{"class":131}," {id ",[70,1347,263],{"class":120},[70,1349,1350],{"class":131}," id, name ",[70,1352,263],{"class":120},[70,1354,1355],{"class":163}," \"Alice\"",[70,1357,1358],{"class":131},"}\n",[70,1360,1362],{"class":72,"line":1361},39,[70,1363,202],{"class":120},[70,1365,1367],{"class":72,"line":1366},40,[70,1368,114],{"emptyLinePlaceholder":113},[70,1370,1372],{"class":72,"line":1371},41,[70,1373,1374],{"class":76},"-- Catch and inspect\n",[70,1376,1378,1380,1382,1384,1386,1389,1392],{"class":72,"line":1377},42,[70,1379,121],{"class":120},[70,1381,329],{"class":131},[70,1383,263],{"class":120},[70,1385,334],{"class":147},[70,1387,1388],{"class":131},"(get_user, ",[70,1390,1391],{"class":163},"\"abc\"",[70,1393,367],{"class":131},[70,1395,1397,1399,1401,1403],{"class":72,"line":1396},43,[70,1398,769],{"class":120},[70,1400,772],{"class":120},[70,1402,775],{"class":131},[70,1404,778],{"class":120},[70,1406,1408,1410,1413,1416,1418,1420],{"class":72,"line":1407},44,[70,1409,138],{"class":120},[70,1411,1412],{"class":147}," getmetatable",[70,1414,1415],{"class":131},"(result) ",[70,1417,144],{"class":120},[70,1419,838],{"class":131},[70,1421,778],{"class":120},[70,1423,1425,1428,1430,1432,1435],{"class":72,"line":1424},45,[70,1426,1427],{"class":147},"    print",[70,1429,160],{"class":131},[70,1431,675],{"class":147},[70,1433,1434],{"class":131},"(result))      ",[70,1436,1437],{"class":76},"-- [CLIENT] VALIDATION: user id must be number\n",[70,1439,1441,1443,1446,1448,1451],{"class":72,"line":1440},46,[70,1442,1427],{"class":147},[70,1444,1445],{"class":131},"(result.",[70,1447,1014],{"class":127},[70,1449,1450],{"class":131},")        ",[70,1452,1453],{"class":76},"-- {provided = \"abc\"}\n",[70,1455,1457],{"class":72,"line":1456},47,[70,1458,1459],{"class":120},"  else\n",[70,1461,1463],{"class":72,"line":1462},48,[70,1464,1465],{"class":76},"    -- unexpected error type (string, etc.)\n",[70,1467,1469,1471,1473,1476,1478,1480],{"class":72,"line":1468},49,[70,1470,157],{"class":147},[70,1472,160],{"class":131},[70,1474,1475],{"class":163},"\"unexpected error: \" ",[70,1477,1144],{"class":120},[70,1479,657],{"class":147},[70,1481,1482],{"class":131},"(result))\n",[70,1484,1486],{"class":72,"line":1485},50,[70,1487,182],{"class":120},[70,1489,1491],{"class":72,"line":1490},51,[70,1492,202],{"class":120},[51,1494,1496,30,1498,30,1500,1503],{"id":1495},"trycatchfinally-simulation",[15,1497,37],{},[15,1499,40],{},[15,1501,1502],{},"finally"," Simulation",[58,1505,1506,1835,1839],{"language":60},[62,1507,1509],{"className":64,"code":1508,"language":60,"meta":66,"style":66},"-- Lua has no try\u002Fcatch. Simulate with pcall + a helper.\n-- The key: `finally` must run whether or not an error occurred.\n\nlocal function try_catch_finally(try_block, catch_block, finally_block)\n  local ok, err = xpcall(try_block, function(e)\n    return e  -- raw error (preserve for catch_block)\n  end)\n  if not ok and catch_block then\n    catch_block(err)\n  end\n  if finally_block then finally_block() end\n  if ok then return err end  -- the result (if success)\n  return nil, err  -- nil + error (if failure and catch handled it)\nend\n\n-- Usage:\nlocal file\nlocal content = try_catch_finally(\n  function()\n    file = io.open(\"data.txt\", \"r\")\n    if not file then error(\"file not found\") end\n    return file:read(\"*a\")\n  end,\n  function(err)\n    print(\"caught: \" .. tostring(err))\n  end,\n  function()\n    if file then file:close() end  -- always cleanup\n  end\n)\nprint(content)  -- file content or nil\n",[15,1510,1511,1516,1521,1525,1537,1557,1567,1573,1588,1595,1599,1616,1635,1648,1652,1656,1661,1668,1682,1689,1711,1735,1755,1761,1767,1783,1789,1795,1817,1821,1825],{"__ignoreMap":66},[70,1512,1513],{"class":72,"line":73},[70,1514,1515],{"class":76},"-- Lua has no try\u002Fcatch. Simulate with pcall + a helper.\n",[70,1517,1518],{"class":72,"line":80},[70,1519,1520],{"class":76},"-- The key: `finally` must run whether or not an error occurred.\n",[70,1522,1523],{"class":72,"line":86},[70,1524,114],{"emptyLinePlaceholder":113},[70,1526,1527,1529,1531,1534],{"class":72,"line":92},[70,1528,121],{"class":120},[70,1530,124],{"class":120},[70,1532,1533],{"class":127}," try_catch_finally",[70,1535,1536],{"class":131},"(try_block, catch_block, finally_block)\n",[70,1538,1539,1542,1545,1547,1549,1552,1554],{"class":72,"line":98},[70,1540,1541],{"class":120},"  local",[70,1543,1544],{"class":131}," ok, err ",[70,1546,263],{"class":120},[70,1548,626],{"class":147},[70,1550,1551],{"class":131},"(try_block, ",[70,1553,339],{"class":120},[70,1555,1556],{"class":131},"(e)\n",[70,1558,1559,1561,1564],{"class":72,"line":104},[70,1560,644],{"class":120},[70,1562,1563],{"class":131}," e  ",[70,1565,1566],{"class":76},"-- raw error (preserve for catch_block)\n",[70,1568,1569,1571],{"class":72,"line":110},[70,1570,709],{"class":120},[70,1572,367],{"class":131},[70,1574,1575,1577,1579,1581,1583,1586],{"class":72,"line":117},[70,1576,138],{"class":120},[70,1578,772],{"class":120},[70,1580,775],{"class":131},[70,1582,462],{"class":120},[70,1584,1585],{"class":131}," catch_block ",[70,1587,778],{"class":120},[70,1589,1590,1593],{"class":72,"line":135},[70,1591,1592],{"class":147},"    catch_block",[70,1594,634],{"class":131},[70,1596,1597],{"class":72,"line":154},[70,1598,182],{"class":120},[70,1600,1601,1603,1606,1609,1612,1614],{"class":72,"line":179},[70,1602,138],{"class":120},[70,1604,1605],{"class":131}," finally_block ",[70,1607,1608],{"class":120},"then",[70,1610,1611],{"class":147}," finally_block",[70,1613,545],{"class":131},[70,1615,202],{"class":120},[70,1617,1618,1620,1622,1624,1627,1630,1632],{"class":72,"line":185},[70,1619,138],{"class":120},[70,1621,775],{"class":131},[70,1623,1608],{"class":120},[70,1625,1626],{"class":120}," return",[70,1628,1629],{"class":131}," err ",[70,1631,356],{"class":120},[70,1633,1634],{"class":76},"  -- the result (if success)\n",[70,1636,1637,1639,1642,1645],{"class":72,"line":199},[70,1638,188],{"class":120},[70,1640,1641],{"class":147}," nil",[70,1643,1644],{"class":131},", err  ",[70,1646,1647],{"class":76},"-- nil + error (if failure and catch handled it)\n",[70,1649,1650],{"class":72,"line":205},[70,1651,202],{"class":120},[70,1653,1654],{"class":72,"line":210},[70,1655,114],{"emptyLinePlaceholder":113},[70,1657,1658],{"class":72,"line":216},[70,1659,1660],{"class":76},"-- Usage:\n",[70,1662,1663,1665],{"class":72,"line":222},[70,1664,121],{"class":120},[70,1666,1667],{"class":131}," file\n",[70,1669,1670,1672,1675,1677,1679],{"class":72,"line":243},[70,1671,121],{"class":120},[70,1673,1674],{"class":131}," content ",[70,1676,263],{"class":120},[70,1678,1533],{"class":147},[70,1680,1681],{"class":131},"(\n",[70,1683,1684,1687],{"class":72,"line":248},[70,1685,1686],{"class":120},"  function",[70,1688,400],{"class":131},[70,1690,1691,1694,1696,1699,1701,1704,1706,1709],{"class":72,"line":254},[70,1692,1693],{"class":131},"    file ",[70,1695,263],{"class":120},[70,1697,1698],{"class":147}," io.open",[70,1700,160],{"class":131},[70,1702,1703],{"class":163},"\"data.txt\"",[70,1705,167],{"class":131},[70,1707,1708],{"class":163},"\"r\"",[70,1710,367],{"class":131},[70,1712,1713,1716,1718,1721,1723,1726,1728,1731,1733],{"class":72,"line":529},[70,1714,1715],{"class":120},"    if",[70,1717,772],{"class":120},[70,1719,1720],{"class":131}," file ",[70,1722,1608],{"class":120},[70,1724,1725],{"class":147}," error",[70,1727,160],{"class":131},[70,1729,1730],{"class":163},"\"file not found\"",[70,1732,555],{"class":131},[70,1734,202],{"class":120},[70,1736,1737,1739,1742,1745,1748,1750,1753],{"class":72,"line":562},[70,1738,644],{"class":120},[70,1740,1741],{"class":127}," file",[70,1743,1744],{"class":131},":",[70,1746,1747],{"class":147},"read",[70,1749,160],{"class":131},[70,1751,1752],{"class":163},"\"*a\"",[70,1754,367],{"class":131},[70,1756,1757,1759],{"class":72,"line":813},[70,1758,709],{"class":120},[70,1760,966],{"class":131},[70,1762,1763,1765],{"class":72,"line":1163},[70,1764,1686],{"class":120},[70,1766,634],{"class":131},[70,1768,1769,1771,1773,1776,1778,1780],{"class":72,"line":1168},[70,1770,1427],{"class":147},[70,1772,160],{"class":131},[70,1774,1775],{"class":163},"\"caught: \" ",[70,1777,1144],{"class":120},[70,1779,657],{"class":147},[70,1781,1782],{"class":131},"(err))\n",[70,1784,1785,1787],{"class":72,"line":1180},[70,1786,709],{"class":120},[70,1788,966],{"class":131},[70,1790,1791,1793],{"class":72,"line":1213},[70,1792,1686],{"class":120},[70,1794,400],{"class":131},[70,1796,1797,1799,1801,1803,1805,1807,1810,1812,1814],{"class":72,"line":1218},[70,1798,1715],{"class":120},[70,1800,1720],{"class":131},[70,1802,1608],{"class":120},[70,1804,1741],{"class":127},[70,1806,1744],{"class":131},[70,1808,1809],{"class":147},"close",[70,1811,545],{"class":131},[70,1813,356],{"class":120},[70,1815,1816],{"class":76},"  -- always cleanup\n",[70,1818,1819],{"class":72,"line":1223},[70,1820,182],{"class":120},[70,1822,1823],{"class":72,"line":1229},[70,1824,367],{"class":131},[70,1826,1827,1829,1832],{"class":72,"line":1242},[70,1828,372],{"class":147},[70,1830,1831],{"class":131},"(content)  ",[70,1833,1834],{"class":76},"-- file content or nil\n",[51,1836,1838],{"id":1837},"error-propagation-with-level-correction","Error Propagation with Level Correction",[58,1840,1841,1987,1993],{"language":60},[62,1842,1844],{"className":64,"code":1843,"language":60,"meta":66,"style":66},"-- When re-raising an error from a pcall, use the correct level so the\n-- error points to the original caller, not the re-raise line.\n\nlocal function log_and_reraise(fn, context)\n  local ok, err = pcall(fn)\n  if not ok then\n    -- Log with full context, then re-raise pointing to the CALLER of this function\n    log_error(context, err)\n    error(err, 2)  -- level 2: blame the caller of log_and_reraise, not this line\n  end\n  return err  -- the result (since ok was true, err holds the return value)\nend\n\n-- Without level 2: error always points to the error() line inside log_and_reraise\n-- With level 2: error points to where log_and_reraise was called\nlog_and_reraise(function() error(\"original\") end, \"processing user input\")\n-- error: \"original\" — traceback points to the log_and_reraise call site\n",[15,1845,1846,1851,1856,1860,1872,1884,1894,1899,1907,1921,1925,1935,1939,1943,1948,1953,1982],{"__ignoreMap":66},[70,1847,1848],{"class":72,"line":73},[70,1849,1850],{"class":76},"-- When re-raising an error from a pcall, use the correct level so the\n",[70,1852,1853],{"class":72,"line":80},[70,1854,1855],{"class":76},"-- error points to the original caller, not the re-raise line.\n",[70,1857,1858],{"class":72,"line":86},[70,1859,114],{"emptyLinePlaceholder":113},[70,1861,1862,1864,1866,1869],{"class":72,"line":92},[70,1863,121],{"class":120},[70,1865,124],{"class":120},[70,1867,1868],{"class":127}," log_and_reraise",[70,1870,1871],{"class":131},"(fn, context)\n",[70,1873,1874,1876,1878,1880,1882],{"class":72,"line":98},[70,1875,1541],{"class":120},[70,1877,1544],{"class":131},[70,1879,263],{"class":120},[70,1881,334],{"class":147},[70,1883,619],{"class":131},[70,1885,1886,1888,1890,1892],{"class":72,"line":104},[70,1887,138],{"class":120},[70,1889,772],{"class":120},[70,1891,775],{"class":131},[70,1893,778],{"class":120},[70,1895,1896],{"class":72,"line":110},[70,1897,1898],{"class":76},"    -- Log with full context, then re-raise pointing to the CALLER of this function\n",[70,1900,1901,1904],{"class":72,"line":117},[70,1902,1903],{"class":147},"    log_error",[70,1905,1906],{"class":131},"(context, err)\n",[70,1908,1909,1911,1914,1916,1918],{"class":72,"line":135},[70,1910,157],{"class":147},[70,1912,1913],{"class":131},"(err, ",[70,1915,170],{"class":147},[70,1917,173],{"class":131},[70,1919,1920],{"class":76},"-- level 2: blame the caller of log_and_reraise, not this line\n",[70,1922,1923],{"class":72,"line":154},[70,1924,182],{"class":120},[70,1926,1927,1929,1932],{"class":72,"line":179},[70,1928,188],{"class":120},[70,1930,1931],{"class":131}," err  ",[70,1933,1934],{"class":76},"-- the result (since ok was true, err holds the return value)\n",[70,1936,1937],{"class":72,"line":185},[70,1938,202],{"class":120},[70,1940,1941],{"class":72,"line":199},[70,1942,114],{"emptyLinePlaceholder":113},[70,1944,1945],{"class":72,"line":205},[70,1946,1947],{"class":76},"-- Without level 2: error always points to the error() line inside log_and_reraise\n",[70,1949,1950],{"class":72,"line":210},[70,1951,1952],{"class":76},"-- With level 2: error points to where log_and_reraise was called\n",[70,1954,1955,1958,1960,1962,1964,1966,1968,1971,1973,1975,1977,1980],{"class":72,"line":216},[70,1956,1957],{"class":147},"log_and_reraise",[70,1959,160],{"class":131},[70,1961,339],{"class":120},[70,1963,545],{"class":131},[70,1965,257],{"class":147},[70,1967,160],{"class":131},[70,1969,1970],{"class":163},"\"original\"",[70,1972,555],{"class":131},[70,1974,356],{"class":120},[70,1976,167],{"class":131},[70,1978,1979],{"class":163},"\"processing user input\"",[70,1981,367],{"class":131},[70,1983,1984],{"class":72,"line":222},[70,1985,1986],{"class":76},"-- error: \"original\" — traceback points to the log_and_reraise call site\n",[51,1988,1990,1992],{"id":1989},"debug-library-introspection",[15,1991,44],{}," Library — Introspection",[58,1994,1995,2465,2469],{"language":60},[62,1996,1998],{"className":64,"code":1997,"language":60,"meta":66,"style":66},"-- debug.getinfo(level_or_fn, what): get info about a function or stack frame\nlocal function outer()\n  local function inner()\n    local info = debug.getinfo(2, \"Slu\")  -- 2 = caller frame; \"Slu\" = Source, lines, Upvalues\n    print(info.source)       -- \"@script.lua\" (or \"stdin\")\n    print(info.currentline)  -- current line in that frame\n    print(info.what)         -- \"Lua\" (or \"C\", \"main\")\n    print(info.nups)        -- number of upvalues\n  end\n  inner()\nend\nouter()\n\n-- debug.getlocal(level, index): get local variable by index in a stack frame\n-- Returns: name, value (or nil if no more locals at that index)\nlocal function dump_locals(level)\n  level = level or 2\n  local i = 1\n  while true do\n    local name, value = debug.getlocal(level, i)\n    if not name then break end\n    print(string.format(\"  %s = %s\", name, tostring(value)))\n    i = i + 1\n  end\nend\n\nlocal function example(a, b)\n  local c = a + b\n  dump_locals(1)  -- dumps: a, b, c (and maybe internal temporaries)\nend\nexample(10, 20)\n\n-- debug.setlocal(level, index, value): set a local variable (powerful, risky)\n-- debug.setupvalue(fn, index, value): set an upvalue of a closure\n\n-- debug.traceback(msg, level): return a string with the stack trace\nprint(debug.traceback(\"custom message\", 2))  -- level 2: skip this line\n\n-- debug.sethook(callback, events, count): set a hook for line\u002Fcall\u002Freturn events\n-- Used for: debuggers, profilers, coverage tools\nlocal function line_hook(event, line)\n  print(\"line \" .. line)  -- fires on every line (slow — debugging only)\nend\ndebug.sethook(line_hook, \"l\")  -- \"l\" = line, \"c\" = call, \"r\" = return\n-- ... code to trace ...\ndebug.sethook()  -- remove hook (pass nothing)\n",[15,1999,2000,2005,2016,2027,2054,2070,2084,2099,2113,2117,2124,2128,2135,2139,2144,2149,2161,2176,2188,2199,2214,2231,2253,2267,2271,2275,2279,2290,2305,2320,2324,2340,2344,2349,2354,2358,2363,2387,2391,2396,2401,2413,2430,2434,2450,2455],{"__ignoreMap":66},[70,2001,2002],{"class":72,"line":73},[70,2003,2004],{"class":76},"-- debug.getinfo(level_or_fn, what): get info about a function or stack frame\n",[70,2006,2007,2009,2011,2014],{"class":72,"line":80},[70,2008,121],{"class":120},[70,2010,124],{"class":120},[70,2012,2013],{"class":127}," outer",[70,2015,400],{"class":131},[70,2017,2018,2020,2022,2025],{"class":72,"line":86},[70,2019,1541],{"class":120},[70,2021,124],{"class":120},[70,2023,2024],{"class":127}," inner",[70,2026,400],{"class":131},[70,2028,2029,2032,2035,2037,2040,2042,2044,2046,2049,2051],{"class":72,"line":92},[70,2030,2031],{"class":120},"    local",[70,2033,2034],{"class":131}," info ",[70,2036,263],{"class":120},[70,2038,2039],{"class":147}," debug.getinfo",[70,2041,160],{"class":131},[70,2043,170],{"class":147},[70,2045,167],{"class":131},[70,2047,2048],{"class":163},"\"Slu\"",[70,2050,173],{"class":131},[70,2052,2053],{"class":76},"-- 2 = caller frame; \"Slu\" = Source, lines, Upvalues\n",[70,2055,2056,2058,2061,2064,2067],{"class":72,"line":98},[70,2057,1427],{"class":147},[70,2059,2060],{"class":131},"(info.",[70,2062,2063],{"class":127},"source",[70,2065,2066],{"class":131},")       ",[70,2068,2069],{"class":76},"-- \"@script.lua\" (or \"stdin\")\n",[70,2071,2072,2074,2076,2079,2081],{"class":72,"line":104},[70,2073,1427],{"class":147},[70,2075,2060],{"class":131},[70,2077,2078],{"class":127},"currentline",[70,2080,173],{"class":131},[70,2082,2083],{"class":76},"-- current line in that frame\n",[70,2085,2086,2088,2090,2093,2096],{"class":72,"line":110},[70,2087,1427],{"class":147},[70,2089,2060],{"class":131},[70,2091,2092],{"class":127},"what",[70,2094,2095],{"class":131},")         ",[70,2097,2098],{"class":76},"-- \"Lua\" (or \"C\", \"main\")\n",[70,2100,2101,2103,2105,2108,2110],{"class":72,"line":117},[70,2102,1427],{"class":147},[70,2104,2060],{"class":131},[70,2106,2107],{"class":127},"nups",[70,2109,1450],{"class":131},[70,2111,2112],{"class":76},"-- number of upvalues\n",[70,2114,2115],{"class":72,"line":135},[70,2116,182],{"class":120},[70,2118,2119,2122],{"class":72,"line":154},[70,2120,2121],{"class":147},"  inner",[70,2123,400],{"class":131},[70,2125,2126],{"class":72,"line":179},[70,2127,202],{"class":120},[70,2129,2130,2133],{"class":72,"line":185},[70,2131,2132],{"class":147},"outer",[70,2134,400],{"class":131},[70,2136,2137],{"class":72,"line":199},[70,2138,114],{"emptyLinePlaceholder":113},[70,2140,2141],{"class":72,"line":205},[70,2142,2143],{"class":76},"-- debug.getlocal(level, index): get local variable by index in a stack frame\n",[70,2145,2146],{"class":72,"line":210},[70,2147,2148],{"class":76},"-- Returns: name, value (or nil if no more locals at that index)\n",[70,2150,2151,2153,2155,2158],{"class":72,"line":216},[70,2152,121],{"class":120},[70,2154,124],{"class":120},[70,2156,2157],{"class":127}," dump_locals",[70,2159,2160],{"class":131},"(level)\n",[70,2162,2163,2166,2168,2171,2173],{"class":72,"line":222},[70,2164,2165],{"class":131},"  level ",[70,2167,263],{"class":120},[70,2169,2170],{"class":131}," level ",[70,2172,467],{"class":120},[70,2174,2175],{"class":147}," 2\n",[70,2177,2178,2180,2183,2185],{"class":72,"line":243},[70,2179,1541],{"class":120},[70,2181,2182],{"class":131}," i ",[70,2184,263],{"class":120},[70,2186,2187],{"class":147}," 1\n",[70,2189,2190,2193,2196],{"class":72,"line":248},[70,2191,2192],{"class":120},"  while",[70,2194,2195],{"class":147}," true",[70,2197,2198],{"class":120}," do\n",[70,2200,2201,2203,2206,2208,2211],{"class":72,"line":254},[70,2202,2031],{"class":120},[70,2204,2205],{"class":131}," name, value ",[70,2207,263],{"class":120},[70,2209,2210],{"class":147}," debug.getlocal",[70,2212,2213],{"class":131},"(level, i)\n",[70,2215,2216,2218,2220,2223,2225,2228],{"class":72,"line":529},[70,2217,1715],{"class":120},[70,2219,772],{"class":120},[70,2221,2222],{"class":131}," name ",[70,2224,1608],{"class":120},[70,2226,2227],{"class":120}," break",[70,2229,2230],{"class":120}," end\n",[70,2232,2233,2235,2237,2240,2242,2245,2248,2250],{"class":72,"line":562},[70,2234,1427],{"class":147},[70,2236,160],{"class":131},[70,2238,2239],{"class":147},"string.format",[70,2241,160],{"class":131},[70,2243,2244],{"class":163},"\"  %s = %s\"",[70,2246,2247],{"class":131},", name, ",[70,2249,675],{"class":147},[70,2251,2252],{"class":131},"(value)))\n",[70,2254,2255,2258,2260,2262,2265],{"class":72,"line":813},[70,2256,2257],{"class":131},"    i ",[70,2259,263],{"class":120},[70,2261,2182],{"class":131},[70,2263,2264],{"class":120},"+",[70,2266,2187],{"class":147},[70,2268,2269],{"class":72,"line":1163},[70,2270,182],{"class":120},[70,2272,2273],{"class":72,"line":1168},[70,2274,202],{"class":120},[70,2276,2277],{"class":72,"line":1180},[70,2278,114],{"emptyLinePlaceholder":113},[70,2280,2281,2283,2285,2288],{"class":72,"line":1213},[70,2282,121],{"class":120},[70,2284,124],{"class":120},[70,2286,2287],{"class":127}," example",[70,2289,132],{"class":131},[70,2291,2292,2294,2297,2299,2301,2303],{"class":72,"line":1218},[70,2293,1541],{"class":120},[70,2295,2296],{"class":131}," c ",[70,2298,263],{"class":120},[70,2300,191],{"class":131},[70,2302,2264],{"class":120},[70,2304,196],{"class":131},[70,2306,2307,2310,2312,2315,2317],{"class":72,"line":1223},[70,2308,2309],{"class":147},"  dump_locals",[70,2311,160],{"class":131},[70,2313,2314],{"class":147},"1",[70,2316,173],{"class":131},[70,2318,2319],{"class":76},"-- dumps: a, b, c (and maybe internal temporaries)\n",[70,2321,2322],{"class":72,"line":1229},[70,2323,202],{"class":120},[70,2325,2326,2329,2331,2333,2335,2338],{"class":72,"line":1242},[70,2327,2328],{"class":147},"example",[70,2330,160],{"class":131},[70,2332,230],{"class":147},[70,2334,167],{"class":131},[70,2336,2337],{"class":147},"20",[70,2339,367],{"class":131},[70,2341,2342],{"class":72,"line":1261},[70,2343,114],{"emptyLinePlaceholder":113},[70,2345,2346],{"class":72,"line":1288},[70,2347,2348],{"class":76},"-- debug.setlocal(level, index, value): set a local variable (powerful, risky)\n",[70,2350,2351],{"class":72,"line":1293},[70,2352,2353],{"class":76},"-- debug.setupvalue(fn, index, value): set an upvalue of a closure\n",[70,2355,2356],{"class":72,"line":1307},[70,2357,114],{"emptyLinePlaceholder":113},[70,2359,2360],{"class":72,"line":1329},[70,2361,2362],{"class":76},"-- debug.traceback(msg, level): return a string with the stack trace\n",[70,2364,2365,2367,2369,2372,2374,2377,2379,2381,2384],{"class":72,"line":1334},[70,2366,372],{"class":147},[70,2368,160],{"class":131},[70,2370,2371],{"class":147},"debug.traceback",[70,2373,160],{"class":131},[70,2375,2376],{"class":163},"\"custom message\"",[70,2378,167],{"class":131},[70,2380,170],{"class":147},[70,2382,2383],{"class":131},"))  ",[70,2385,2386],{"class":76},"-- level 2: skip this line\n",[70,2388,2389],{"class":72,"line":1340},[70,2390,114],{"emptyLinePlaceholder":113},[70,2392,2393],{"class":72,"line":1361},[70,2394,2395],{"class":76},"-- debug.sethook(callback, events, count): set a hook for line\u002Fcall\u002Freturn events\n",[70,2397,2398],{"class":72,"line":1366},[70,2399,2400],{"class":76},"-- Used for: debuggers, profilers, coverage tools\n",[70,2402,2403,2405,2407,2410],{"class":72,"line":1371},[70,2404,121],{"class":120},[70,2406,124],{"class":120},[70,2408,2409],{"class":127}," line_hook",[70,2411,2412],{"class":131},"(event, line)\n",[70,2414,2415,2417,2419,2422,2424,2427],{"class":72,"line":1377},[70,2416,783],{"class":147},[70,2418,160],{"class":131},[70,2420,2421],{"class":163},"\"line \" ",[70,2423,1144],{"class":120},[70,2425,2426],{"class":131}," line)  ",[70,2428,2429],{"class":76},"-- fires on every line (slow — debugging only)\n",[70,2431,2432],{"class":72,"line":1396},[70,2433,202],{"class":120},[70,2435,2436,2439,2442,2445,2447],{"class":72,"line":1407},[70,2437,2438],{"class":147},"debug.sethook",[70,2440,2441],{"class":131},"(line_hook, ",[70,2443,2444],{"class":163},"\"l\"",[70,2446,173],{"class":131},[70,2448,2449],{"class":76},"-- \"l\" = line, \"c\" = call, \"r\" = return\n",[70,2451,2452],{"class":72,"line":1424},[70,2453,2454],{"class":76},"-- ... code to trace ...\n",[70,2456,2457,2459,2462],{"class":72,"line":1440},[70,2458,2438],{"class":147},[70,2460,2461],{"class":131},"()  ",[70,2463,2464],{"class":76},"-- remove hook (pass nothing)\n",[51,2466,2468],{"id":2467},"production-retry-with-exponential-backoff","Production: Retry with Exponential Backoff",[58,2470,2471,2970,2974],{"language":60},[62,2472,2474],{"className":64,"code":2473,"language":60,"meta":66,"style":66},"local function retry(fn, opts)\n  opts = opts or {}\n  local max_attempts = opts.max_attempts or 3\n  local base_delay = opts.base_delay or 0.1  -- 100ms\n  local max_delay = opts.max_delay or 10     -- 10s cap\n  local should_retry = opts.should_retry or function(err) return true end\n\n  local last_err\n  for attempt = 1, max_attempts do\n    local ok, result = pcall(fn, attempt)\n    if ok then return result end\n    last_err = result\n    if not should_retry(result) then break end  -- non-retryable error\n    if attempt \u003C max_attempts then\n      local delay = math.min(base_delay * (2 ^ (attempt - 1)), max_delay)  -- exponential\n      if opts.on_retry then opts.on_retry(attempt, delay, result) end\n      os.execute(\"sleep \" .. tostring(delay))  -- or a proper sleep function\n    end\n  end\n  return nil, last_err  -- all attempts failed\nend\n\n-- Usage: retry a flaky network call\nlocal result, err = retry(function(attempt)\n  return fetch_url(\"https:\u002F\u002Fapi.example.com\u002Fdata\")  -- may fail transiently\nend, {\n  max_attempts = 5,\n  base_delay = 0.5,\n  should_retry = function(err)\n    -- Only retry on connection errors (not 4xx client errors)\n    return tostring(err):match(\"timeout\") or tostring(err):match(\"connection\")\n  end,\n  on_retry = function(attempt, delay, err)\n    print(string.format(\"attempt %d failed, retrying in %.1fs: %s\", attempt, delay, err))\n  end,\n})\n",[15,2475,2476,2488,2502,2521,2543,2565,2592,2596,2603,2622,2635,2650,2660,2681,2693,2734,2756,2776,2781,2785,2797,2801,2805,2810,2828,2845,2852,2864,2876,2887,2892,2926,2932,2944,2960,2966],{"__ignoreMap":66},[70,2477,2478,2480,2482,2485],{"class":72,"line":73},[70,2479,121],{"class":120},[70,2481,124],{"class":120},[70,2483,2484],{"class":127}," retry",[70,2486,2487],{"class":131},"(fn, opts)\n",[70,2489,2490,2493,2495,2498,2500],{"class":72,"line":80},[70,2491,2492],{"class":131},"  opts ",[70,2494,263],{"class":120},[70,2496,2497],{"class":131}," opts ",[70,2499,467],{"class":120},[70,2501,843],{"class":131},[70,2503,2504,2506,2509,2511,2513,2516,2518],{"class":72,"line":86},[70,2505,1541],{"class":120},[70,2507,2508],{"class":131}," max_attempts ",[70,2510,263],{"class":120},[70,2512,955],{"class":131},[70,2514,2515],{"class":127},"max_attempts",[70,2517,960],{"class":120},[70,2519,2520],{"class":147}," 3\n",[70,2522,2523,2525,2528,2530,2532,2535,2537,2540],{"class":72,"line":92},[70,2524,1541],{"class":120},[70,2526,2527],{"class":131}," base_delay ",[70,2529,263],{"class":120},[70,2531,955],{"class":131},[70,2533,2534],{"class":127},"base_delay",[70,2536,960],{"class":120},[70,2538,2539],{"class":147}," 0.1",[70,2541,2542],{"class":76},"  -- 100ms\n",[70,2544,2545,2547,2550,2552,2554,2557,2559,2562],{"class":72,"line":98},[70,2546,1541],{"class":120},[70,2548,2549],{"class":131}," max_delay ",[70,2551,263],{"class":120},[70,2553,955],{"class":131},[70,2555,2556],{"class":127},"max_delay",[70,2558,960],{"class":120},[70,2560,2561],{"class":147}," 10",[70,2563,2564],{"class":76},"     -- 10s cap\n",[70,2566,2567,2569,2572,2574,2576,2579,2581,2583,2586,2588,2590],{"class":72,"line":104},[70,2568,1541],{"class":120},[70,2570,2571],{"class":131}," should_retry ",[70,2573,263],{"class":120},[70,2575,955],{"class":131},[70,2577,2578],{"class":127},"should_retry",[70,2580,960],{"class":120},[70,2582,124],{"class":120},[70,2584,2585],{"class":131},"(err) ",[70,2587,452],{"class":120},[70,2589,2195],{"class":147},[70,2591,2230],{"class":120},[70,2593,2594],{"class":72,"line":110},[70,2595,114],{"emptyLinePlaceholder":113},[70,2597,2598,2600],{"class":72,"line":117},[70,2599,1541],{"class":120},[70,2601,2602],{"class":131}," last_err\n",[70,2604,2605,2608,2611,2613,2616,2619],{"class":72,"line":135},[70,2606,2607],{"class":120},"  for",[70,2609,2610],{"class":131}," attempt ",[70,2612,263],{"class":120},[70,2614,2615],{"class":147}," 1",[70,2617,2618],{"class":131},", max_attempts ",[70,2620,2621],{"class":120},"do\n",[70,2623,2624,2626,2628,2630,2632],{"class":72,"line":154},[70,2625,2031],{"class":120},[70,2627,329],{"class":131},[70,2629,263],{"class":120},[70,2631,334],{"class":147},[70,2633,2634],{"class":131},"(fn, attempt)\n",[70,2636,2637,2639,2641,2643,2645,2648],{"class":72,"line":179},[70,2638,1715],{"class":120},[70,2640,775],{"class":131},[70,2642,1608],{"class":120},[70,2644,1626],{"class":120},[70,2646,2647],{"class":131}," result ",[70,2649,202],{"class":120},[70,2651,2652,2655,2657],{"class":72,"line":185},[70,2653,2654],{"class":131},"    last_err ",[70,2656,263],{"class":120},[70,2658,2659],{"class":131}," result\n",[70,2661,2662,2664,2666,2669,2671,2673,2675,2678],{"class":72,"line":199},[70,2663,1715],{"class":120},[70,2665,772],{"class":120},[70,2667,2668],{"class":147}," should_retry",[70,2670,1415],{"class":131},[70,2672,1608],{"class":120},[70,2674,2227],{"class":120},[70,2676,2677],{"class":120}," end",[70,2679,2680],{"class":76},"  -- non-retryable error\n",[70,2682,2683,2685,2687,2689,2691],{"class":72,"line":205},[70,2684,1715],{"class":120},[70,2686,2610],{"class":131},[70,2688,457],{"class":120},[70,2690,2508],{"class":131},[70,2692,778],{"class":120},[70,2694,2695,2698,2701,2703,2706,2709,2712,2715,2717,2720,2723,2726,2728,2731],{"class":72,"line":210},[70,2696,2697],{"class":120},"      local",[70,2699,2700],{"class":131}," delay ",[70,2702,263],{"class":120},[70,2704,2705],{"class":147}," math.min",[70,2707,2708],{"class":131},"(base_delay ",[70,2710,2711],{"class":120},"*",[70,2713,2714],{"class":131}," (",[70,2716,170],{"class":147},[70,2718,2719],{"class":120}," ^",[70,2721,2722],{"class":131}," (attempt ",[70,2724,2725],{"class":120},"-",[70,2727,2615],{"class":147},[70,2729,2730],{"class":131},")), max_delay)  ",[70,2732,2733],{"class":76},"-- exponential\n",[70,2735,2736,2739,2741,2744,2747,2749,2751,2754],{"class":72,"line":216},[70,2737,2738],{"class":120},"      if",[70,2740,955],{"class":131},[70,2742,2743],{"class":127},"on_retry",[70,2745,2746],{"class":120}," then",[70,2748,955],{"class":131},[70,2750,2743],{"class":147},[70,2752,2753],{"class":131},"(attempt, delay, result) ",[70,2755,202],{"class":120},[70,2757,2758,2761,2763,2766,2768,2770,2773],{"class":72,"line":222},[70,2759,2760],{"class":147},"      os.execute",[70,2762,160],{"class":131},[70,2764,2765],{"class":163},"\"sleep \" ",[70,2767,1144],{"class":120},[70,2769,657],{"class":147},[70,2771,2772],{"class":131},"(delay))  ",[70,2774,2775],{"class":76},"-- or a proper sleep function\n",[70,2777,2778],{"class":72,"line":243},[70,2779,2780],{"class":120},"    end\n",[70,2782,2783],{"class":72,"line":248},[70,2784,182],{"class":120},[70,2786,2787,2789,2791,2794],{"class":72,"line":254},[70,2788,188],{"class":120},[70,2790,1641],{"class":147},[70,2792,2793],{"class":131},", last_err  ",[70,2795,2796],{"class":76},"-- all attempts failed\n",[70,2798,2799],{"class":72,"line":529},[70,2800,202],{"class":120},[70,2802,2803],{"class":72,"line":562},[70,2804,114],{"emptyLinePlaceholder":113},[70,2806,2807],{"class":72,"line":813},[70,2808,2809],{"class":76},"-- Usage: retry a flaky network call\n",[70,2811,2812,2814,2817,2819,2821,2823,2825],{"class":72,"line":1163},[70,2813,121],{"class":120},[70,2815,2816],{"class":131}," result, err ",[70,2818,263],{"class":120},[70,2820,2484],{"class":147},[70,2822,160],{"class":131},[70,2824,339],{"class":120},[70,2826,2827],{"class":131},"(attempt)\n",[70,2829,2830,2832,2835,2837,2840,2842],{"class":72,"line":1168},[70,2831,188],{"class":120},[70,2833,2834],{"class":147}," fetch_url",[70,2836,160],{"class":131},[70,2838,2839],{"class":163},"\"https:\u002F\u002Fapi.example.com\u002Fdata\"",[70,2841,173],{"class":131},[70,2843,2844],{"class":76},"-- may fail transiently\n",[70,2846,2847,2849],{"class":72,"line":1180},[70,2848,356],{"class":120},[70,2850,2851],{"class":131},", {\n",[70,2853,2854,2857,2859,2862],{"class":72,"line":1213},[70,2855,2856],{"class":131},"  max_attempts ",[70,2858,263],{"class":120},[70,2860,2861],{"class":147}," 5",[70,2863,966],{"class":131},[70,2865,2866,2869,2871,2874],{"class":72,"line":1218},[70,2867,2868],{"class":131},"  base_delay ",[70,2870,263],{"class":120},[70,2872,2873],{"class":147}," 0.5",[70,2875,966],{"class":131},[70,2877,2878,2881,2883,2885],{"class":72,"line":1223},[70,2879,2880],{"class":127},"  should_retry",[70,2882,854],{"class":120},[70,2884,124],{"class":120},[70,2886,634],{"class":131},[70,2888,2889],{"class":72,"line":1229},[70,2890,2891],{"class":76},"    -- Only retry on connection errors (not 4xx client errors)\n",[70,2893,2894,2896,2898,2901,2904,2906,2909,2911,2913,2915,2917,2919,2921,2924],{"class":72,"line":1242},[70,2895,644],{"class":120},[70,2897,657],{"class":147},[70,2899,2900],{"class":131},"(err):",[70,2902,2903],{"class":147},"match",[70,2905,160],{"class":131},[70,2907,2908],{"class":163},"\"timeout\"",[70,2910,555],{"class":131},[70,2912,467],{"class":120},[70,2914,657],{"class":147},[70,2916,2900],{"class":131},[70,2918,2903],{"class":147},[70,2920,160],{"class":131},[70,2922,2923],{"class":163},"\"connection\"",[70,2925,367],{"class":131},[70,2927,2928,2930],{"class":72,"line":1261},[70,2929,709],{"class":120},[70,2931,966],{"class":131},[70,2933,2934,2937,2939,2941],{"class":72,"line":1288},[70,2935,2936],{"class":127},"  on_retry",[70,2938,854],{"class":120},[70,2940,124],{"class":120},[70,2942,2943],{"class":131},"(attempt, delay, err)\n",[70,2945,2946,2948,2950,2952,2954,2957],{"class":72,"line":1293},[70,2947,1427],{"class":147},[70,2949,160],{"class":131},[70,2951,2239],{"class":147},[70,2953,160],{"class":131},[70,2955,2956],{"class":163},"\"attempt %d failed, retrying in %.1fs: %s\"",[70,2958,2959],{"class":131},", attempt, delay, err))\n",[70,2961,2962,2964],{"class":72,"line":1307},[70,2963,709],{"class":120},[70,2965,966],{"class":131},[70,2967,2968],{"class":72,"line":1329},[70,2969,758],{"class":131},[51,2971,2973],{"id":2972},"production-circuit-breaker","Production: Circuit Breaker",[58,2975,2976,3521,3527],{"language":60},[62,2977,2979],{"className":64,"code":2978,"language":60,"meta":66,"style":66},"-- Circuit breaker: stop calling a failing service after N failures, try again after cooldown\nlocal CircuitBreaker = {}\nCircuitBreaker.__index = CircuitBreaker\n\nfunction CircuitBreaker.new(opts)\n  return setmetatable({\n    failure_count = 0,\n    failure_threshold = opts.failure_threshold or 5,\n    cooldown = opts.cooldown or 30,  -- seconds\n    last_failure_time = 0,\n    state = \"closed\",  -- \"closed\", \"open\", \"half_open\"\n  }, CircuitBreaker)\nend\n\nfunction CircuitBreaker:call(fn)\n  if self.state == \"open\" then\n    if os.time() - self.last_failure_time >= self.cooldown then\n      self.state = \"half_open\"  -- try once\n    else\n      return nil, \"circuit open\"  -- fail fast\n    end\n  end\n\n  local ok, result = pcall(fn)\n  if ok then\n    self.failure_count = 0\n    self.state = \"closed\"\n    return result\n  else\n    self.failure_count = self.failure_count + 1\n    self.last_failure_time = os.time()\n    if self.failure_count >= self.failure_threshold then\n      self.state = \"open\"\n    end\n    return nil, result  -- the error\n  end\nend\n\n-- Usage\nlocal breaker = CircuitBreaker.new({failure_threshold = 3, cooldown = 60})\nfor i = 1, 10 do\n  local result, err = breaker:call(function()\n    return fetch_from_service()  -- may fail\n  end)\n  if not result then print(\"call \" .. i .. \" failed: \" .. tostring(err)) end\nend\n",[15,2980,2981,2986,2997,3009,3013,3026,3034,3045,3063,3085,3096,3111,3116,3120,3124,3137,3157,3185,3202,3207,3222,3226,3230,3234,3246,3254,3269,3282,3288,3292,3313,3327,3347,3360,3364,3376,3380,3384,3388,3393,3425,3442,3463,3475,3481,3517],{"__ignoreMap":66},[70,2982,2983],{"class":72,"line":73},[70,2984,2985],{"class":76},"-- Circuit breaker: stop calling a failing service after N failures, try again after cooldown\n",[70,2987,2988,2990,2993,2995],{"class":72,"line":80},[70,2989,121],{"class":120},[70,2991,2992],{"class":131}," CircuitBreaker ",[70,2994,263],{"class":120},[70,2996,843],{"class":131},[70,2998,2999,3002,3004,3006],{"class":72,"line":86},[70,3000,3001],{"class":131},"CircuitBreaker.",[70,3003,851],{"class":127},[70,3005,854],{"class":120},[70,3007,3008],{"class":131}," CircuitBreaker\n",[70,3010,3011],{"class":72,"line":92},[70,3012,114],{"emptyLinePlaceholder":113},[70,3014,3015,3017,3020,3022,3024],{"class":72,"line":98},[70,3016,339],{"class":120},[70,3018,3019],{"class":127}," CircuitBreaker",[70,3021,891],{"class":131},[70,3023,932],{"class":127},[70,3025,935],{"class":131},[70,3027,3028,3030,3032],{"class":72,"line":104},[70,3029,188],{"class":120},[70,3031,942],{"class":147},[70,3033,945],{"class":131},[70,3035,3036,3039,3041,3043],{"class":72,"line":110},[70,3037,3038],{"class":131},"    failure_count ",[70,3040,263],{"class":120},[70,3042,148],{"class":147},[70,3044,966],{"class":131},[70,3046,3047,3050,3052,3054,3057,3059,3061],{"class":72,"line":117},[70,3048,3049],{"class":131},"    failure_threshold ",[70,3051,263],{"class":120},[70,3053,955],{"class":131},[70,3055,3056],{"class":127},"failure_threshold",[70,3058,960],{"class":120},[70,3060,2861],{"class":147},[70,3062,966],{"class":131},[70,3064,3065,3068,3070,3072,3075,3077,3080,3082],{"class":72,"line":135},[70,3066,3067],{"class":131},"    cooldown ",[70,3069,263],{"class":120},[70,3071,955],{"class":131},[70,3073,3074],{"class":127},"cooldown",[70,3076,960],{"class":120},[70,3078,3079],{"class":147}," 30",[70,3081,1034],{"class":131},[70,3083,3084],{"class":76},"-- seconds\n",[70,3086,3087,3090,3092,3094],{"class":72,"line":154},[70,3088,3089],{"class":131},"    last_failure_time ",[70,3091,263],{"class":120},[70,3093,148],{"class":147},[70,3095,966],{"class":131},[70,3097,3098,3101,3103,3106,3108],{"class":72,"line":179},[70,3099,3100],{"class":131},"    state ",[70,3102,263],{"class":120},[70,3104,3105],{"class":163}," \"closed\"",[70,3107,1034],{"class":131},[70,3109,3110],{"class":76},"-- \"closed\", \"open\", \"half_open\"\n",[70,3112,3113],{"class":72,"line":185},[70,3114,3115],{"class":131},"  }, CircuitBreaker)\n",[70,3117,3118],{"class":72,"line":199},[70,3119,202],{"class":120},[70,3121,3122],{"class":72,"line":205},[70,3123,114],{"emptyLinePlaceholder":113},[70,3125,3126,3128,3130,3132,3135],{"class":72,"line":210},[70,3127,339],{"class":120},[70,3129,3019],{"class":127},[70,3131,1744],{"class":131},[70,3133,3134],{"class":127},"call",[70,3136,619],{"class":131},[70,3138,3139,3141,3144,3146,3149,3152,3155],{"class":72,"line":216},[70,3140,138],{"class":120},[70,3142,3143],{"class":147}," self",[70,3145,891],{"class":131},[70,3147,3148],{"class":127},"state",[70,3150,3151],{"class":120}," ==",[70,3153,3154],{"class":163}," \"open\" ",[70,3156,778],{"class":120},[70,3158,3159,3161,3163,3165,3167,3169,3171,3174,3177,3179,3181,3183],{"class":72,"line":222},[70,3160,1715],{"class":120},[70,3162,696],{"class":147},[70,3164,545],{"class":131},[70,3166,2725],{"class":120},[70,3168,3143],{"class":147},[70,3170,891],{"class":131},[70,3172,3173],{"class":127},"last_failure_time",[70,3175,3176],{"class":120}," >=",[70,3178,3143],{"class":147},[70,3180,891],{"class":131},[70,3182,3074],{"class":127},[70,3184,151],{"class":120},[70,3186,3187,3190,3192,3194,3196,3199],{"class":72,"line":243},[70,3188,3189],{"class":147},"      self",[70,3191,891],{"class":131},[70,3193,3148],{"class":127},[70,3195,854],{"class":120},[70,3197,3198],{"class":163}," \"half_open\"  ",[70,3200,3201],{"class":76},"-- try once\n",[70,3203,3204],{"class":72,"line":248},[70,3205,3206],{"class":120},"    else\n",[70,3208,3209,3212,3214,3216,3219],{"class":72,"line":254},[70,3210,3211],{"class":120},"      return",[70,3213,1641],{"class":147},[70,3215,167],{"class":131},[70,3217,3218],{"class":163},"\"circuit open\"  ",[70,3220,3221],{"class":76},"-- fail fast\n",[70,3223,3224],{"class":72,"line":529},[70,3225,2780],{"class":120},[70,3227,3228],{"class":72,"line":562},[70,3229,182],{"class":120},[70,3231,3232],{"class":72,"line":813},[70,3233,114],{"emptyLinePlaceholder":113},[70,3235,3236,3238,3240,3242,3244],{"class":72,"line":1163},[70,3237,1541],{"class":120},[70,3239,329],{"class":131},[70,3241,263],{"class":120},[70,3243,334],{"class":147},[70,3245,619],{"class":131},[70,3247,3248,3250,3252],{"class":72,"line":1168},[70,3249,138],{"class":120},[70,3251,775],{"class":131},[70,3253,778],{"class":120},[70,3255,3256,3259,3261,3264,3266],{"class":72,"line":1180},[70,3257,3258],{"class":147},"    self",[70,3260,891],{"class":131},[70,3262,3263],{"class":127},"failure_count",[70,3265,854],{"class":120},[70,3267,3268],{"class":147}," 0\n",[70,3270,3271,3273,3275,3277,3279],{"class":72,"line":1213},[70,3272,3258],{"class":147},[70,3274,891],{"class":131},[70,3276,3148],{"class":127},[70,3278,854],{"class":120},[70,3280,3281],{"class":163}," \"closed\"\n",[70,3283,3284,3286],{"class":72,"line":1218},[70,3285,644],{"class":120},[70,3287,2659],{"class":131},[70,3289,3290],{"class":72,"line":1223},[70,3291,1459],{"class":120},[70,3293,3294,3296,3298,3300,3302,3304,3306,3308,3311],{"class":72,"line":1229},[70,3295,3258],{"class":147},[70,3297,891],{"class":131},[70,3299,3263],{"class":127},[70,3301,854],{"class":120},[70,3303,3143],{"class":147},[70,3305,891],{"class":131},[70,3307,3263],{"class":127},[70,3309,3310],{"class":120}," +",[70,3312,2187],{"class":147},[70,3314,3315,3317,3319,3321,3323,3325],{"class":72,"line":1242},[70,3316,3258],{"class":147},[70,3318,891],{"class":131},[70,3320,3173],{"class":127},[70,3322,854],{"class":120},[70,3324,696],{"class":147},[70,3326,400],{"class":131},[70,3328,3329,3331,3333,3335,3337,3339,3341,3343,3345],{"class":72,"line":1261},[70,3330,1715],{"class":120},[70,3332,3143],{"class":147},[70,3334,891],{"class":131},[70,3336,3263],{"class":127},[70,3338,3176],{"class":120},[70,3340,3143],{"class":147},[70,3342,891],{"class":131},[70,3344,3056],{"class":127},[70,3346,151],{"class":120},[70,3348,3349,3351,3353,3355,3357],{"class":72,"line":1288},[70,3350,3189],{"class":147},[70,3352,891],{"class":131},[70,3354,3148],{"class":127},[70,3356,854],{"class":120},[70,3358,3359],{"class":163}," \"open\"\n",[70,3361,3362],{"class":72,"line":1293},[70,3363,2780],{"class":120},[70,3365,3366,3368,3370,3373],{"class":72,"line":1307},[70,3367,644],{"class":120},[70,3369,1641],{"class":147},[70,3371,3372],{"class":131},", result  ",[70,3374,3375],{"class":76},"-- the error\n",[70,3377,3378],{"class":72,"line":1329},[70,3379,182],{"class":120},[70,3381,3382],{"class":72,"line":1334},[70,3383,202],{"class":120},[70,3385,3386],{"class":72,"line":1340},[70,3387,114],{"emptyLinePlaceholder":113},[70,3389,3390],{"class":72,"line":1361},[70,3391,3392],{"class":76},"-- Usage\n",[70,3394,3395,3397,3400,3402,3405,3407,3410,3412,3415,3418,3420,3423],{"class":72,"line":1366},[70,3396,121],{"class":120},[70,3398,3399],{"class":131}," breaker ",[70,3401,263],{"class":120},[70,3403,3404],{"class":131}," CircuitBreaker.",[70,3406,932],{"class":147},[70,3408,3409],{"class":131},"({failure_threshold ",[70,3411,263],{"class":120},[70,3413,3414],{"class":147}," 3",[70,3416,3417],{"class":131},", cooldown ",[70,3419,263],{"class":120},[70,3421,3422],{"class":147}," 60",[70,3424,758],{"class":131},[70,3426,3427,3430,3432,3434,3436,3438,3440],{"class":72,"line":1371},[70,3428,3429],{"class":120},"for",[70,3431,2182],{"class":131},[70,3433,263],{"class":120},[70,3435,2615],{"class":147},[70,3437,167],{"class":131},[70,3439,230],{"class":147},[70,3441,2198],{"class":120},[70,3443,3444,3446,3448,3450,3453,3455,3457,3459,3461],{"class":72,"line":1377},[70,3445,1541],{"class":120},[70,3447,2816],{"class":131},[70,3449,263],{"class":120},[70,3451,3452],{"class":127}," breaker",[70,3454,1744],{"class":131},[70,3456,3134],{"class":147},[70,3458,160],{"class":131},[70,3460,339],{"class":120},[70,3462,400],{"class":131},[70,3464,3465,3467,3470,3472],{"class":72,"line":1396},[70,3466,644],{"class":120},[70,3468,3469],{"class":147}," fetch_from_service",[70,3471,2461],{"class":131},[70,3473,3474],{"class":76},"-- may fail\n",[70,3476,3477,3479],{"class":72,"line":1407},[70,3478,709],{"class":120},[70,3480,367],{"class":131},[70,3482,3483,3485,3487,3489,3491,3494,3496,3499,3501,3503,3505,3508,3510,3512,3515],{"class":72,"line":1424},[70,3484,138],{"class":120},[70,3486,772],{"class":120},[70,3488,2647],{"class":131},[70,3490,1608],{"class":120},[70,3492,3493],{"class":147}," print",[70,3495,160],{"class":131},[70,3497,3498],{"class":163},"\"call \" ",[70,3500,1144],{"class":120},[70,3502,2182],{"class":131},[70,3504,1144],{"class":120},[70,3506,3507],{"class":163}," \" failed: \" ",[70,3509,1144],{"class":120},[70,3511,657],{"class":147},[70,3513,3514],{"class":131},"(err)) ",[70,3516,202],{"class":120},[70,3518,3519],{"class":72,"line":1440},[70,3520,202],{"class":120},[51,3522,3524,3526],{"id":3523},"close-for-deterministic-cleanup-lua-54",[15,3525,48],{}," for Deterministic Cleanup (Lua 5.4+)",[58,3528,3529,3832,3836,3850],{"language":60},[62,3530,3532],{"className":64,"code":3531,"language":60,"meta":66,"style":66},"-- \u003Cclose> ensures cleanup runs when the variable goes out of scope\n-- Works with any object that has a __close metamethod (or a function value)\n-- Runs even on error (like defer\u002Ffinally, not like GC-based finalization)\n\nlocal function with_file(path, fn)\n  local f \u003Cclose> = assert(io.open(path, \"r\"))  -- auto-close on scope exit\n  return fn(f)\n  -- f:close() called here automatically (even if fn errors)\nend\n\nlocal content = with_file(\"data.txt\", function(f)\n  return f:read(\"*a\")\nend)\n-- File guaranteed closed, even if the function body threw an error\n\n-- Custom closeable object:\nlocal function acquire_lock(name)\n  local lock = {name = name}\n  local mt = {\n    __close = function(self)\n      print(\"releasing lock: \" .. self.name)\n      release_lock(self.name)  -- your cleanup logic\n    end,\n  }\n  return setmetatable(lock, mt)\nend\n\ndo\n  local lock \u003Cclose> = acquire_lock(\"resource1\")\n  use_locked_resource()\n  -- lock released here automatically (deterministic, not GC-based)\nend\n",[15,3533,3534,3539,3544,3549,3553,3565,3594,3604,3609,3613,3617,3637,3654,3660,3665,3669,3674,3686,3703,3714,3725,3746,3764,3771,3776,3785,3789,3793,3797,3816,3823,3828],{"__ignoreMap":66},[70,3535,3536],{"class":72,"line":73},[70,3537,3538],{"class":76},"-- \u003Cclose> ensures cleanup runs when the variable goes out of scope\n",[70,3540,3541],{"class":72,"line":80},[70,3542,3543],{"class":76},"-- Works with any object that has a __close metamethod (or a function value)\n",[70,3545,3546],{"class":72,"line":86},[70,3547,3548],{"class":76},"-- Runs even on error (like defer\u002Ffinally, not like GC-based finalization)\n",[70,3550,3551],{"class":72,"line":92},[70,3552,114],{"emptyLinePlaceholder":113},[70,3554,3555,3557,3559,3562],{"class":72,"line":98},[70,3556,121],{"class":120},[70,3558,124],{"class":120},[70,3560,3561],{"class":127}," with_file",[70,3563,3564],{"class":131},"(path, fn)\n",[70,3566,3567,3569,3572,3574,3576,3579,3581,3584,3587,3589,3591],{"class":72,"line":104},[70,3568,1541],{"class":120},[70,3570,3571],{"class":131}," f ",[70,3573,48],{"class":120},[70,3575,854],{"class":120},[70,3577,3578],{"class":147}," assert",[70,3580,160],{"class":131},[70,3582,3583],{"class":147},"io.open",[70,3585,3586],{"class":131},"(path, ",[70,3588,1708],{"class":163},[70,3590,2383],{"class":131},[70,3592,3593],{"class":76},"-- auto-close on scope exit\n",[70,3595,3596,3598,3601],{"class":72,"line":110},[70,3597,188],{"class":120},[70,3599,3600],{"class":147}," fn",[70,3602,3603],{"class":131},"(f)\n",[70,3605,3606],{"class":72,"line":117},[70,3607,3608],{"class":76},"  -- f:close() called here automatically (even if fn errors)\n",[70,3610,3611],{"class":72,"line":135},[70,3612,202],{"class":120},[70,3614,3615],{"class":72,"line":154},[70,3616,114],{"emptyLinePlaceholder":113},[70,3618,3619,3621,3623,3625,3627,3629,3631,3633,3635],{"class":72,"line":179},[70,3620,121],{"class":120},[70,3622,1674],{"class":131},[70,3624,263],{"class":120},[70,3626,3561],{"class":147},[70,3628,160],{"class":131},[70,3630,1703],{"class":163},[70,3632,167],{"class":131},[70,3634,339],{"class":120},[70,3636,3603],{"class":131},[70,3638,3639,3641,3644,3646,3648,3650,3652],{"class":72,"line":185},[70,3640,188],{"class":120},[70,3642,3643],{"class":127}," f",[70,3645,1744],{"class":131},[70,3647,1747],{"class":147},[70,3649,160],{"class":131},[70,3651,1752],{"class":163},[70,3653,367],{"class":131},[70,3655,3656,3658],{"class":72,"line":199},[70,3657,356],{"class":120},[70,3659,367],{"class":131},[70,3661,3662],{"class":72,"line":205},[70,3663,3664],{"class":76},"-- File guaranteed closed, even if the function body threw an error\n",[70,3666,3667],{"class":72,"line":210},[70,3668,114],{"emptyLinePlaceholder":113},[70,3670,3671],{"class":72,"line":216},[70,3672,3673],{"class":76},"-- Custom closeable object:\n",[70,3675,3676,3678,3680,3683],{"class":72,"line":222},[70,3677,121],{"class":120},[70,3679,124],{"class":120},[70,3681,3682],{"class":127}," acquire_lock",[70,3684,3685],{"class":131},"(name)\n",[70,3687,3688,3690,3693,3695,3698,3700],{"class":72,"line":243},[70,3689,1541],{"class":120},[70,3691,3692],{"class":131}," lock ",[70,3694,263],{"class":120},[70,3696,3697],{"class":131}," {name ",[70,3699,263],{"class":120},[70,3701,3702],{"class":131}," name}\n",[70,3704,3705,3707,3710,3712],{"class":72,"line":248},[70,3706,1541],{"class":120},[70,3708,3709],{"class":131}," mt ",[70,3711,263],{"class":120},[70,3713,647],{"class":131},[70,3715,3716,3719,3721,3723],{"class":72,"line":254},[70,3717,3718],{"class":127},"    __close",[70,3720,854],{"class":120},[70,3722,124],{"class":120},[70,3724,871],{"class":131},[70,3726,3727,3730,3732,3735,3737,3739,3741,3744],{"class":72,"line":529},[70,3728,3729],{"class":147},"      print",[70,3731,160],{"class":131},[70,3733,3734],{"class":163},"\"releasing lock: \" ",[70,3736,1144],{"class":120},[70,3738,3143],{"class":147},[70,3740,891],{"class":131},[70,3742,3743],{"class":127},"name",[70,3745,367],{"class":131},[70,3747,3748,3751,3753,3755,3757,3759,3761],{"class":72,"line":562},[70,3749,3750],{"class":147},"      release_lock",[70,3752,160],{"class":131},[70,3754,888],{"class":147},[70,3756,891],{"class":131},[70,3758,3743],{"class":127},[70,3760,173],{"class":131},[70,3762,3763],{"class":76},"-- your cleanup logic\n",[70,3765,3766,3769],{"class":72,"line":813},[70,3767,3768],{"class":120},"    end",[70,3770,966],{"class":131},[70,3772,3773],{"class":72,"line":1163},[70,3774,3775],{"class":131},"  }\n",[70,3777,3778,3780,3782],{"class":72,"line":1168},[70,3779,188],{"class":120},[70,3781,942],{"class":147},[70,3783,3784],{"class":131},"(lock, mt)\n",[70,3786,3787],{"class":72,"line":1180},[70,3788,202],{"class":120},[70,3790,3791],{"class":72,"line":1213},[70,3792,114],{"emptyLinePlaceholder":113},[70,3794,3795],{"class":72,"line":1218},[70,3796,2621],{"class":120},[70,3798,3799,3801,3803,3805,3807,3809,3811,3814],{"class":72,"line":1223},[70,3800,1541],{"class":120},[70,3802,3692],{"class":131},[70,3804,48],{"class":120},[70,3806,854],{"class":120},[70,3808,3682],{"class":147},[70,3810,160],{"class":131},[70,3812,3813],{"class":163},"\"resource1\"",[70,3815,367],{"class":131},[70,3817,3818,3821],{"class":72,"line":1229},[70,3819,3820],{"class":147},"  use_locked_resource",[70,3822,400],{"class":131},[70,3824,3825],{"class":72,"line":1242},[70,3826,3827],{"class":76},"  -- lock released here automatically (deterministic, not GC-based)\n",[70,3829,3830],{"class":72,"line":1261},[70,3831,202],{"class":120},[51,3833,3835],{"id":3834},"tips-tricks","💡 Tips & Tricks",[20,3837,3838,3845,3846,3849],{},[3839,3840,3841,3844],"strong",{},[15,3842,3843],{},"assert"," returns its arguments on success",": ",[15,3847,3848],{},"local f = assert(io.open(\"file\"))"," — assert returns the file handle if truthy.",[58,3851,3852],{"language":60},[62,3853,3855],{"className":64,"code":3854,"language":60,"meta":66,"style":66},"local f = assert(io.open(\"data.txt\", \"r\"))  -- returns f if truthy, errors if nil\n-- Equivalent to:\nlocal f = io.open(\"data.txt\", \"r\")\nif not f then error(\"cannot open data.txt\") end\n",[15,3856,3857,3884,3889,3909],{"__ignoreMap":66},[70,3858,3859,3861,3863,3865,3867,3869,3871,3873,3875,3877,3879,3881],{"class":72,"line":73},[70,3860,121],{"class":120},[70,3862,3571],{"class":131},[70,3864,263],{"class":120},[70,3866,3578],{"class":147},[70,3868,160],{"class":131},[70,3870,3583],{"class":147},[70,3872,160],{"class":131},[70,3874,1703],{"class":163},[70,3876,167],{"class":131},[70,3878,1708],{"class":163},[70,3880,2383],{"class":131},[70,3882,3883],{"class":76},"-- returns f if truthy, errors if nil\n",[70,3885,3886],{"class":72,"line":80},[70,3887,3888],{"class":76},"-- Equivalent to:\n",[70,3890,3891,3893,3895,3897,3899,3901,3903,3905,3907],{"class":72,"line":86},[70,3892,121],{"class":120},[70,3894,3571],{"class":131},[70,3896,263],{"class":120},[70,3898,1698],{"class":147},[70,3900,160],{"class":131},[70,3902,1703],{"class":163},[70,3904,167],{"class":131},[70,3906,1708],{"class":163},[70,3908,367],{"class":131},[70,3910,3911,3913,3915,3917,3919,3921,3923,3926,3928],{"class":72,"line":92},[70,3912,769],{"class":120},[70,3914,772],{"class":120},[70,3916,3571],{"class":131},[70,3918,1608],{"class":120},[70,3920,1725],{"class":147},[70,3922,160],{"class":131},[70,3924,3925],{"class":163},"\"cannot open data.txt\"",[70,3927,555],{"class":131},[70,3929,202],{"class":120},[20,3931,3932,3939,3940,891],{},[3839,3933,3934,3935,3938],{},"Use ",[15,3936,3937],{},"error(err, 0)"," to suppress position prefix",": When the error message already contains location info, level 0 skips adding ",[15,3941,3942],{},"file:line:",[58,3944,3945],{"language":60},[62,3946,3948],{"className":64,"code":3947,"language":60,"meta":66,"style":66},"error(\"custom error without file:line prefix\", 0)  -- just \"custom error...\"\n",[15,3949,3950],{"__ignoreMap":66},[70,3951,3952,3954,3956,3959,3961,3963,3965],{"class":72,"line":73},[70,3953,257],{"class":147},[70,3955,160],{"class":131},[70,3957,3958],{"class":163},"\"custom error without file:line prefix\"",[70,3960,167],{"class":131},[70,3962,235],{"class":147},[70,3964,173],{"class":131},[70,3966,3967],{"class":76},"-- just \"custom error...\"\n",[20,3969,3970,3976],{},[3839,3971,3972,3975],{},[15,3973,3974],{},"xpcall"," handler can modify the error",": Transform errors before they reach the caller.",[58,3978,3979],{"language":60},[62,3980,3982],{"className":64,"code":3981,"language":60,"meta":66,"style":66},"local ok, err = xpcall(function() error(\"raw error\") end, function(e)\n  return \"wrapped: \" .. tostring(e)  -- transform the error\nend)\nprint(err)  -- \"wrapped: stdin:LINE: raw error\"\n",[15,3983,3984,4017,4034,4040],{"__ignoreMap":66},[70,3985,3986,3988,3990,3992,3994,3996,3998,4000,4002,4004,4007,4009,4011,4013,4015],{"class":72,"line":73},[70,3987,121],{"class":120},[70,3989,1544],{"class":131},[70,3991,263],{"class":120},[70,3993,626],{"class":147},[70,3995,160],{"class":131},[70,3997,339],{"class":120},[70,3999,545],{"class":131},[70,4001,257],{"class":147},[70,4003,160],{"class":131},[70,4005,4006],{"class":163},"\"raw error\"",[70,4008,555],{"class":131},[70,4010,356],{"class":120},[70,4012,167],{"class":131},[70,4014,339],{"class":120},[70,4016,1556],{"class":131},[70,4018,4019,4021,4024,4026,4028,4031],{"class":72,"line":80},[70,4020,188],{"class":120},[70,4022,4023],{"class":163}," \"wrapped: \" ",[70,4025,1144],{"class":120},[70,4027,657],{"class":147},[70,4029,4030],{"class":131},"(e)  ",[70,4032,4033],{"class":76},"-- transform the error\n",[70,4035,4036,4038],{"class":72,"line":86},[70,4037,356],{"class":120},[70,4039,367],{"class":131},[70,4041,4042,4044,4047],{"class":72,"line":92},[70,4043,372],{"class":147},[70,4045,4046],{"class":131},"(err)  ",[70,4048,4049],{"class":76},"-- \"wrapped: stdin:LINE: raw error\"\n",[51,4051,4053],{"id":4052},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[20,4055,4056,4065],{},[3839,4057,4058,4060,4061,4064],{},[15,4059,17],{}," errors with table objects don't get ",[15,4062,4063],{},"file:line"," prefix",": Only string errors get the prefix. Tables, numbers, and other values are passed as-is.",[58,4067,4068],{"language":60},[62,4069,4071],{"className":64,"code":4070,"language":60,"meta":66,"style":66},"error(\"string error\")      -- pcall returns: false, \"file:line: string error\"\nerror({code = 1})          -- pcall returns: false, {code = 1} (NO prefix — table)\nerror(42)                  -- pcall returns: false, 42 (NO prefix — number)\n",[15,4072,4073,4088,4104],{"__ignoreMap":66},[70,4074,4075,4077,4079,4082,4085],{"class":72,"line":73},[70,4076,257],{"class":147},[70,4078,160],{"class":131},[70,4080,4081],{"class":163},"\"string error\"",[70,4083,4084],{"class":131},")      ",[70,4086,4087],{"class":76},"-- pcall returns: false, \"file:line: string error\"\n",[70,4089,4090,4092,4094,4096,4098,4101],{"class":72,"line":80},[70,4091,257],{"class":147},[70,4093,260],{"class":131},[70,4095,263],{"class":120},[70,4097,2615],{"class":147},[70,4099,4100],{"class":131},"})          ",[70,4102,4103],{"class":76},"-- pcall returns: false, {code = 1} (NO prefix — table)\n",[70,4105,4106,4108,4110,4113,4116],{"class":72,"line":86},[70,4107,257],{"class":147},[70,4109,160],{"class":131},[70,4111,4112],{"class":147},"42",[70,4114,4115],{"class":131},")                  ",[70,4117,4118],{"class":76},"-- pcall returns: false, 42 (NO prefix — number)\n",[20,4120,4121,4126,4127,4130],{},[3839,4122,4123,4125],{},[15,4124,3974],{}," handler errors cause a second error",": If the handler itself errors, xpcall returns ",[15,4128,4129],{},"false"," with a message about the handler error, not the original.",[20,4132,4133,4136,4137,4140],{},[3839,4134,4135],{},"Stack traces are only available in the handler",": After pcall returns, the stack is unwound — ",[15,4138,4139],{},"debug.traceback()"," in the pcall result gives a useless trace.",[20,4142,4143,3845,4148,4151,4152,4155,4156,4159],{},[3839,4144,4145,4147],{},[15,4146,3843],{}," evaluates its message argument eagerly",[15,4149,4150],{},"assert(cond, expensive_fn())"," calls ",[15,4153,4154],{},"expensive_fn()"," even when ",[15,4157,4158],{},"cond"," is true. Use a closure for lazy messages if expensive.",[58,4161,4162],{"language":60},[62,4163,4165],{"className":64,"code":4164,"language":60,"meta":66,"style":66},"-- BAD: expensive_fn() always runs\nassert(x, expensive_format(x))\n-- OK: lazy evaluation only on failure\nassert(x) or error(expensive_format(x))\n-- or:\nif not x then error(expensive_format(x), 2) end\n",[15,4166,4167,4172,4185,4190,4207,4212],{"__ignoreMap":66},[70,4168,4169],{"class":72,"line":73},[70,4170,4171],{"class":76},"-- BAD: expensive_fn() always runs\n",[70,4173,4174,4176,4179,4182],{"class":72,"line":80},[70,4175,3843],{"class":147},[70,4177,4178],{"class":131},"(x, ",[70,4180,4181],{"class":147},"expensive_format",[70,4183,4184],{"class":131},"(x))\n",[70,4186,4187],{"class":72,"line":86},[70,4188,4189],{"class":76},"-- OK: lazy evaluation only on failure\n",[70,4191,4192,4194,4197,4199,4201,4203,4205],{"class":72,"line":92},[70,4193,3843],{"class":147},[70,4195,4196],{"class":131},"(x) ",[70,4198,467],{"class":120},[70,4200,1725],{"class":147},[70,4202,160],{"class":131},[70,4204,4181],{"class":147},[70,4206,4184],{"class":131},[70,4208,4209],{"class":72,"line":98},[70,4210,4211],{"class":76},"-- or:\n",[70,4213,4214,4216,4218,4221,4223,4225,4227,4229,4232,4234,4236],{"class":72,"line":104},[70,4215,769],{"class":120},[70,4217,772],{"class":120},[70,4219,4220],{"class":131}," x ",[70,4222,1608],{"class":120},[70,4224,1725],{"class":147},[70,4226,160],{"class":131},[70,4228,4181],{"class":147},[70,4230,4231],{"class":131},"(x), ",[70,4233,170],{"class":147},[70,4235,555],{"class":131},[70,4237,202],{"class":120},[51,4239,4241],{"id":4240},"spot-the-bug","🧠 Spot the Bug",[58,4243,4244,4347],{"language":60},[62,4245,4247],{"className":64,"code":4246,"language":60,"meta":66,"style":66},"local function risky()\n  error(\"failed\")\nend\n\nlocal function handler()\n  local ok, err = pcall(risky)\n  print(debug.traceback(\"trace\", 2))  -- try to get caller's trace\n  return ok, err\nend\n\nhandler()\n",[15,4248,4249,4260,4271,4275,4279,4290,4303,4325,4332,4336,4340],{"__ignoreMap":66},[70,4250,4251,4253,4255,4258],{"class":72,"line":73},[70,4252,121],{"class":120},[70,4254,124],{"class":120},[70,4256,4257],{"class":127}," risky",[70,4259,400],{"class":131},[70,4261,4262,4264,4266,4269],{"class":72,"line":80},[70,4263,405],{"class":147},[70,4265,160],{"class":131},[70,4267,4268],{"class":163},"\"failed\"",[70,4270,367],{"class":131},[70,4272,4273],{"class":72,"line":86},[70,4274,202],{"class":120},[70,4276,4277],{"class":72,"line":92},[70,4278,114],{"emptyLinePlaceholder":113},[70,4280,4281,4283,4285,4288],{"class":72,"line":98},[70,4282,121],{"class":120},[70,4284,124],{"class":120},[70,4286,4287],{"class":127}," handler",[70,4289,400],{"class":131},[70,4291,4292,4294,4296,4298,4300],{"class":72,"line":104},[70,4293,1541],{"class":120},[70,4295,1544],{"class":131},[70,4297,263],{"class":120},[70,4299,334],{"class":147},[70,4301,4302],{"class":131},"(risky)\n",[70,4304,4305,4307,4309,4311,4313,4316,4318,4320,4322],{"class":72,"line":110},[70,4306,783],{"class":147},[70,4308,160],{"class":131},[70,4310,2371],{"class":147},[70,4312,160],{"class":131},[70,4314,4315],{"class":163},"\"trace\"",[70,4317,167],{"class":131},[70,4319,170],{"class":147},[70,4321,2383],{"class":131},[70,4323,4324],{"class":76},"-- try to get caller's trace\n",[70,4326,4327,4329],{"class":72,"line":117},[70,4328,188],{"class":120},[70,4330,4331],{"class":131}," ok, err\n",[70,4333,4334],{"class":72,"line":135},[70,4335,202],{"class":120},[70,4337,4338],{"class":72,"line":154},[70,4339,114],{"emptyLinePlaceholder":113},[70,4341,4342,4345],{"class":72,"line":179},[70,4343,4344],{"class":147},"handler",[70,4346,400],{"class":131},[4348,4349,4350,4354,4381,4398,4509],"details",{},[4351,4352,4353],"summary",{},"Answer",[20,4355,4356,4357,4359,4360,4363,4364,4366,4367,4370,4371,4373,4374,4376,4377,4380],{},"The ",[15,4358,2371],{}," call produces a ",[3839,4361,4362],{},"useless trace"," — by the time ",[15,4365,17],{}," returns, the stack from ",[15,4368,4369],{},"risky()"," has already been unwound. The trace only shows ",[15,4372,4344],{}," → ",[15,4375,17],{}," → (nothing from ",[15,4378,4379],{},"risky",").",[20,4382,4383,4384,4387,4388,4390,4391,4393,4394,4397],{},"To capture the ",[3839,4385,4386],{},"original"," stack trace, you must use ",[15,4389,3974],{}," with a handler that calls ",[15,4392,2371],{}," ",[3839,4395,4396],{},"before"," the stack unwinds:",[62,4399,4401],{"className":64,"code":4400,"language":60,"meta":66,"style":66},"local function risky() error(\"failed\") end\n\nlocal function handler()\n  local ok, err = xpcall(risky, function(e)\n    return debug.traceback(tostring(e), 2)  -- capture NOW, stack still alive\n  end)\n  print(err)  -- full trace including risky's frame\n  return ok, err\nend\n\nhandler()\n",[15,4402,4403,4423,4427,4437,4454,4474,4480,4489,4495,4499,4503],{"__ignoreMap":66},[70,4404,4405,4407,4409,4411,4413,4415,4417,4419,4421],{"class":72,"line":73},[70,4406,121],{"class":120},[70,4408,124],{"class":120},[70,4410,4257],{"class":127},[70,4412,545],{"class":131},[70,4414,257],{"class":147},[70,4416,160],{"class":131},[70,4418,4268],{"class":163},[70,4420,555],{"class":131},[70,4422,202],{"class":120},[70,4424,4425],{"class":72,"line":80},[70,4426,114],{"emptyLinePlaceholder":113},[70,4428,4429,4431,4433,4435],{"class":72,"line":86},[70,4430,121],{"class":120},[70,4432,124],{"class":120},[70,4434,4287],{"class":127},[70,4436,400],{"class":131},[70,4438,4439,4441,4443,4445,4447,4450,4452],{"class":72,"line":92},[70,4440,1541],{"class":120},[70,4442,1544],{"class":131},[70,4444,263],{"class":120},[70,4446,626],{"class":147},[70,4448,4449],{"class":131},"(risky, ",[70,4451,339],{"class":120},[70,4453,1556],{"class":131},[70,4455,4456,4458,4460,4462,4464,4467,4469,4471],{"class":72,"line":98},[70,4457,644],{"class":120},[70,4459,670],{"class":147},[70,4461,160],{"class":131},[70,4463,675],{"class":147},[70,4465,4466],{"class":131},"(e), ",[70,4468,170],{"class":147},[70,4470,173],{"class":131},[70,4472,4473],{"class":76},"-- capture NOW, stack still alive\n",[70,4475,4476,4478],{"class":72,"line":104},[70,4477,709],{"class":120},[70,4479,367],{"class":131},[70,4481,4482,4484,4486],{"class":72,"line":110},[70,4483,783],{"class":147},[70,4485,4046],{"class":131},[70,4487,4488],{"class":76},"-- full trace including risky's frame\n",[70,4490,4491,4493],{"class":72,"line":117},[70,4492,188],{"class":120},[70,4494,4331],{"class":131},[70,4496,4497],{"class":72,"line":135},[70,4498,202],{"class":120},[70,4500,4501],{"class":72,"line":154},[70,4502,114],{"emptyLinePlaceholder":113},[70,4504,4505,4507],{"class":72,"line":179},[70,4506,4344],{"class":147},[70,4508,400],{"class":131},[20,4510,4511,4512,4514,4515,4517],{},"The key insight: ",[15,4513,17],{}," unwinds the stack before returning control. Only ",[15,4516,3974],{},"'s handler runs while the stack is still intact.",[4519,4520,4521],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}",{"title":66,"searchDepth":80,"depth":80,"links":4523},[4524,4526,4528,4529],{"id":53,"depth":80,"text":4525},"error() — Throwing",{"id":293,"depth":80,"text":4527},"pcall() — Protected Call",{"id":4052,"depth":80,"text":4053},{"id":4240,"depth":80,"text":4241},"[object Object]","md",{},"\u002Flua\u002F09-error-handling",{"title":5,"description":4535},{"Deep-dive into Lua's error model":4536},"error() with levels and objects, pcall\u002Fxpcall semantics, structured error types with metatables, try\u002Fcatch\u002Ffinally simulation, error propagation with traceback preservation, debug library introspection, and production patterns for retry, circuit breakers, and resource cleanup with \u003Cclose>.","lua\u002F09-error-handling","V7SPltrdolm0a-NnEletftiTWPJePAWBdo6K2iAG0uQ",1789924650356]