0Pricing
Lua Academy · Lección

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

Comprobació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
end

Jerarquí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"))     -- false

Envoltorio 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))
end

Có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 denied

Errores 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)
end

Impresió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
end

Aserciones 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) end

Comprobació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 __tostring para 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

  1. La función error()
  2. Llamadas protegidas con pcall
  3. xpcall y gestores de mensajes
  4. Objetos de error estructurados
← Volver a Lua Academy