Objetos de error estructurados
Pase tablas como objetos de error para transmitir el tipo y el contexto.
Objetos de error estructurados es una lección gratuita de Lua Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Lua Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Lua Academy incluye 4 lecciones en total.
¿Por qué usar errores estructurados?
Los errores de texto simple son difíciles de gestionar mediante programación. Los objetos de error estructurados (tablas) contienen información de tipo y datos de contexto, y quienes llamen pueden inspeccionarlos y actuar en consecuencia. Esto permite dirigir la gestión según el error sin analizar cadenas.
-- Plain string: hard to handle programmatically
error("database error: connection refused")
-- Structured: type + data
error({
type = "DatabaseError",
code = "CONN_REFUSED",
host = "localhost",
port = 5432,
message = "connection refused"
})Patrón de constructor de errores
Cree una función de fábrica para cada tipo de error. La fábrica construye una tabla con campos coherentes: tipo, mensaje y cualquier contexto relevante. Un metamétodo __tostring hace que el error se muestre correctamente.
local ErrorMT = {__tostring = function(e)
return string.format("[%s] %s", e.type, e.message)
end}
local function makeError(errType, msg, data)
local e = {type=errType, message=msg}
if data then for k,v in pairs(data) do e[k]=v end end
return setmetatable(e, ErrorMT)
end
local E = {
notFound = function(name) return makeError("NOT_FOUND","not found: "..name,{name=name}) end,
badInput = function(msg,field) return makeError("BAD_INPUT",msg,{field=field}) end,
}
local ok, err = pcall(error, E.notFound("user:42"))
print(tostring(err)) -- [NOT_FOUND] not found: user:42Comprobación del tipo de los objetos de error
Después de capturar un error, compruebe si es una tabla con un campo de tipo conocido. Esto permite seleccionar distintas estrategias de recuperación según el tipo de error, sin depender de patrones de cadenas.
local function handleRequest(fn)
local ok, err = pcall(fn)
if ok then return true end
if type(err) == "table" then
if err.type == "NOT_FOUND" then
print("404: " .. err.message)
elseif err.type == "BAD_INPUT" then
print("400: " .. err.message .. " (field: " .. (err.field or "?") .. ")")
else
print("500: unhandled error: " .. tostring(err))
end
else
print("500: " .. tostring(err))
end
return false
endJerarquía de errores
Simule una jerarquía de errores comprobando los campos is_a o usando metatablas. Los tipos de error secundarios heredan los campos del tipo principal y el código que no necesita detalles específicos puede tratarlos como el tipo principal.
local function isError(e, errType)
if type(e) ~= "table" then return false end
return e.type == errType or e.parentType == errType
end
local function makeDbError(code, msg)
return {type="DbError:"..code, parentType="DbError", code=code, message=msg}
end
local err = makeDbError("TIMEOUT","query timed out")
print(isError(err, "DbError")) -- true
print(isError(err, "DbError:TIMEOUT")) -- true
print(isError(err, "NetworkError")) -- falseEnvoltorio de errores
Al capturar y volver a lanzar un error, envuelva el error original para añadir contexto sin perderlo. El envoltorio tiene su propio tipo y conserva el error original como causa.
local function wrapError(msg, cause)
return {
type = "WrappedError",
message = msg,
cause = cause,
}
end
local function loadConfig(path)
local ok, err = pcall(function()
local f = assert(io.open(path,"r"))
local content = f:read("a")
f:close()
return content
end)
if not ok then
error(wrapError("failed to load config: "..path, err))
end
end
local ok2, e = pcall(loadConfig, "missing.cfg")
if not ok2 then
print(e.message)
print("Caused by:", tostring(e.cause))
endCódigos de error frente a tipos de error
Existen dos convenciones habituales: códigos de error (numéricos, como los códigos de estado HTTP) y cadenas de tipo de error (nombres semánticos). Los códigos de error son fáciles de comparar numéricamente; las cadenas de tipo se explican por sí mismas. Muchos sistemas usan ambos.
local STATUS = {OK=200, NOT_FOUND=404, SERVER_ERROR=500, BAD_REQUEST=400}
local function makeStatusError(status, msg)
return {status=status, message=msg, type="HTTPError"}
end
local function handleError(e)
if e.status == STATUS.NOT_FOUND then
print("Resource not found:", e.message)
elseif e.status >= 500 then
print("Server error:", e.message)
else
print("Error", e.status, e.message)
end
end
handleError(makeStatusError(404, "user not found"))Pila de errores (cadena de causas)
Cuando un error es causado por otro, encadénelos. Esto ofrece una visión completa de lo que salió mal en cada capa de la aplicación. Deshaga la cadena para registrar o mostrar toda la historia del error.
local function unwindCause(e, depth)
depth = depth or 0
local pad = string.rep(" ", depth)
if type(e) == "table" then
print(pad .. (e.type or "Error") .. ": " .. (e.message or "?"))
if e.cause then unwindCause(e.cause, depth+1) end
else
print(pad .. tostring(e))
end
end
local inner = {type="IoError", message="permission denied"}
local outer = {type="ConfigError", message="cannot load config", cause=inner}
unwindCause(outer)
-- ConfigError: cannot load config
-- IoError: permission deniedErrores en el contexto de una devolución de llamada
Cuando se producen errores dentro de devoluciones de llamada (controladores de eventos o iteradores), se propagan a quien llama a la devolución de llamada. Use pcall para capturarlos e informar con contexto sobre qué devolución de llamada falló.
local function runCallbacks(callbacks, data)
local errors = {}
for name, fn in pairs(callbacks) do
local ok, err = pcall(fn, data)
if not ok then
errors[#errors+1] = {callback=name, error=err}
end
end
return errors
end
local cbs = {
validate = function(d) assert(d.name, "name required") end,
transform = function(d) d.name = d.name:upper() end,
}
local errs = runCallbacks(cbs, {})
for _, e in ipairs(errs) do
print(e.callback, "->", e.error)
endImpresión de detalles de errores
Una función auxiliar que imprime un objeto de error de forma estructurada y legible, gestionando tanto errores de tipo cadena como errores de tipo tabla. Resulta útil en los límites de la aplicación, donde los errores se registran o se muestran a los usuarios.
local function printError(err, prefix)
prefix = prefix or "Error"
if type(err) ~= "table" then
print(prefix .. ": " .. tostring(err))
return
end
print(prefix .. " [" .. (err.type or "unknown") .. "]")
print(" Message: " .. (err.message or "?"))
for k, v in pairs(err) do
if k ~= "type" and k ~= "message" and k ~= "cause" then
print(" " .. k .. ": " .. tostring(v))
end
end
if err.cause then printError(err.cause, " Caused by") end
endAserciones con errores estructurados
Cree un assertT (aserción con errores tipados) que lance un error estructurado en lugar de una cadena simple. Esto facilita que quienes llamen puedan comprobar tipos de error específicos.
local function assertT(cond, errType, msg, data)
if not cond then
local e = {type=errType, message=msg}
if data then for k,v in pairs(data) do e[k]=v end end
error(e, 2)
end
return cond
end
local function createUser(name, age)
assertT(type(name)=="string", "BAD_INPUT", "name must be string", {field="name"})
assertT(age >= 0 and age <= 150, "BAD_INPUT", "invalid age", {field="age", value=age})
return {name=name, age=age}
end
local ok, err = pcall(createUser, "Alice", -5)
if not ok then print(err.type, err.field, err.value) endComprobación rápida
¿Cuál es la principal ventaja de pasar una tabla a error() en lugar de una cadena?
Resumen: errores estructurados
Resumen:
- Pase tablas a
error()para obtener errores estructurados e inspeccionables - Incluya el tipo, el mensaje y los campos de contexto relevantes
- Añada
__tostringpara obtener una salida legible - Envuelva los errores para añadir contexto sin perder la causa
- Seleccione la gestión según el tipo: compruebe
err.type, no patrones de cadenas
Preguntas frecuentes
¿La lección «Objetos de error estructurados» es gratis?
Sí — el texto completo de «Objetos de error estructurados» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Lua Academy, actualiza a CoddyKit PRO. El curso de Lua Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Objetos de error estructurados»?
Pase tablas como objetos de error para transmitir el tipo y el contexto. Practicas Lua Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Lua Academy?
No se requiere experiencia previa. Lua Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.
¿Cuánto tiempo toma la lección «Objetos de error estructurados»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Lua Academy?
Sí. Cada lección de Lua Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- La función error()
- Llamadas protegidas con pcall
- xpcall y gestores de mensajes
- Objetos de error estructurados