Objetos de erro estruturados
Passe tabelas como objetos de erro para transmitir tipo e contexto.
Objetos de erro estruturados é uma aula grátis de Lua Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Lua Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Lua Academy inclui 4 aulas no total.
Por que usar erros estruturados?
Erros simples em strings são difíceis de tratar programaticamente. Objetos de erro estruturados (tabelas) contêm informações de tipo e dados de contexto, e podem ser inspecionados e tratados pelos chamadores. Isso permite despachar com base em erros sem analisar strings.
-- 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"
})Padrão de construtor de erros
Crie uma função fábrica de erros para cada tipo de erro. A fábrica constrói uma tabela com campos consistentes: tipo, mensagem e qualquer contexto relevante. Um metamétodo __tostring faz com que o erro seja exibido de forma adequada.
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:42Verificação de tipo de objetos de erro
Depois de capturar um erro, verifique se ele é uma tabela com um campo de tipo conhecido. Isso permite escolher diferentes estratégias de recuperação com base no tipo de erro, sem depender da correspondência de padrões em strings.
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
endHierarquia de erros
Simule uma hierarquia de erros verificando campos is_a ou usando metatabelas. Os tipos de erro filhos herdam os campos do tipo pai e podem ser tratados como o pai pelo código que não precisa de detalhes específicos.
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")) -- falseEnvolvimento de erros
Ao capturar e gerar novamente um erro, envolva o erro original para adicionar contexto sem perdê-lo. O envoltório tem seu próprio tipo e contém o erro 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 erro versus tipos de erro
Duas convenções comuns são: códigos de erro (numéricos, como códigos de status HTTP) e strings de tipo de erro (nomes semânticos). Os códigos de erro são fáceis de comparar numericamente; as strings de tipo são autoexplicativas. Muitos sistemas usam 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"))Pilha de erros (cadeia de causas)
Quando um erro é causado por outro, encadeie-os. Isso fornece uma visão completa do que deu errado em cada camada da aplicação. Desfaça a cadeia para registrar ou exibir toda a história do erro.
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 deniedErro no contexto de uma função de retorno
Quando ocorrem erros dentro de funções de retorno (manipuladores de eventos, iteradores), eles se propagam para o chamador da função de retorno. Use pcall para capturá-los e relatá-los com contexto sobre qual função de retorno falhou.
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)
endExibição dos detalhes do erro
Um auxiliar que exibe um objeto de erro de maneira estruturada e legível, tratando tanto erros em strings quanto erros em tabelas. Isso é útil nos limites da aplicação, onde os erros são registrados ou exibidos aos usuários.
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
endAsserções com erros estruturados
Crie um assertT (assert com erros tipados) que gere um erro estruturado em vez de uma string simples. Isso facilita testar tipos de erro específicos nos chamadores.
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) endVerificação rápida
Qual é a principal vantagem de passar uma tabela para error() em vez de uma string?
Recapitulação: erros estruturados
Resumo:
- Passe tabelas para
error()para obter erros estruturados e inspecionáveis - Inclua: tipo, mensagem e campos de contexto relevantes
- Adicione
__tostringpara obter uma saída legível - Envolva os erros para adicionar contexto sem perder a causa
- Escolha o tratamento pelo tipo: verifique
err.type, não padrões de string
Perguntas Frequentes
A aula “Objetos de erro estruturados” é grátis?
Sim — o texto completo de “Objetos de erro estruturados” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Lua Academy, atualize para CoddyKit PRO. O curso de Lua Academy inclui 4 aulas no total.
O que vou aprender em “Objetos de erro estruturados”?
Passe tabelas como objetos de erro para transmitir tipo e contexto. Você pratica Lua Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Lua Academy?
Nenhuma experiência prévia é necessária. Lua Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “Objetos de erro estruturados”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Lua Academy?
Sim. Cada aula de Lua Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- A função error()
- Chamadas protegidas com pcall
- xpcall e manipuladores de mensagens
- Objetos de erro estruturados