Objets d’erreur structurés
Transmettez des tables comme objets d’erreur pour communiquer le type et le contexte.
Objets d’erreur structurés est une leçon Lua Academy gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Lua Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Lua Academy comprend 4 leçons au total.
Pourquoi utiliser des erreurs structurées ?
Les erreurs sous forme de simples chaînes sont difficiles à traiter par programmation. Les objets d’erreur structurés (tables) contiennent des informations de type et des données de contexte ; les appelants peuvent les examiner et agir en conséquence. Cela permet de distribuer le traitement selon l’erreur sans analyser des chaînes.
-- 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"
})Schéma de constructeur d’erreurs
Créez une fonction fabrique d’erreurs pour chaque type d’erreur. La fabrique construit une table avec des champs cohérents : type, message et tout contexte pertinent. Une métaméthode __tostring permet d’afficher correctement l’erreur.
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:42Vérifier le type des objets d’erreur
Après avoir capturé une erreur, vérifiez qu’il s’agit d’une table contenant un champ de type connu. Vous pouvez ainsi distribuer différentes stratégies de récupération selon le type d’erreur, sans dépendre de la recherche de motifs dans des chaînes.
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
endHiérarchie d’erreurs
Simulez une hiérarchie d’erreurs en vérifiant les champs is_a ou en utilisant des métatables. Les types d’erreurs enfants héritent des champs du type parent et peuvent être traités comme le parent par le code qui n’a pas besoin des détails.
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")) -- falseEncapsuler les erreurs
Lors de la capture et de la relance d’une erreur, encapsulez l’erreur d’origine pour ajouter du contexte sans la perdre. L’encapsuleur possède son propre type et conserve l’erreur d’origine comme cause.
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))
endCodes d’erreur ou types d’erreur
Deux conventions sont courantes : les codes d’erreur (numériques, comme les codes d’état HTTP) et les chaînes de type d’erreur (noms sémantiques). Les codes d’erreur sont faciles à comparer numériquement ; les chaînes de type sont explicites. De nombreux systèmes utilisent les deux.
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"))Pile d’erreurs (chaîne des causes)
Lorsqu’une erreur est causée par une autre, reliez-les. Vous obtenez ainsi une vue complète de ce qui s’est mal passé à chaque couche de l’application. Défaites la chaîne pour journaliser ou afficher l’historique complet de l’erreur.
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 deniedErreur dans le contexte d’un rappel
Lorsque des erreurs surviennent dans des rappels (gestionnaires d’événements, itérateurs), elles se propagent jusqu’à l’appelant du rappel. Utilisez pcall pour les intercepter et les signaler avec le contexte indiquant quel rappel a échoué.
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)
endAfficher les détails d’une erreur
Un utilitaire qui affiche un objet d’erreur de manière structurée et lisible, en gérant à la fois les erreurs sous forme de chaîne et celles sous forme de table. Il est utile aux limites de l’application, là où les erreurs sont journalisées ou affichées aux utilisateurs.
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
endAssertions avec des erreurs structurées
Créez un assertT (assertion avec erreurs typées) qui déclenche une erreur structurée au lieu d’une simple chaîne. Il devient ainsi facile de tester des types d’erreur précis dans le code appelant.
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) endVérification rapide
Quel est le principal avantage de transmettre une table à error() plutôt qu’une chaîne ?
Récapitulatif : erreurs structurées
Résumé :
- Transmettez des tables à
error()pour obtenir des erreurs structurées et inspectables - Incluez : le type, le message et les champs de contexte pertinents
- Ajoutez
__tostringpour obtenir un affichage lisible - Encapsulez les erreurs pour ajouter du contexte sans perdre la cause
- Distribuez le traitement selon le type dans les gestionnaires : vérifiez
err.type, et non des motifs de chaîne
Questions Fréquemment Posées
La leçon « Objets d’erreur structurés » est-elle gratuite ?
Oui — le texte complet de « Objets d’erreur structurés » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Lua Academy, passe à CoddyKit PRO. Le cours Lua Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Objets d’erreur structurés » ?
Transmettez des tables comme objets d’erreur pour communiquer le type et le contexte. Tu pratiques Lua Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer Lua Academy ?
Aucune expérience préalable n'est requise. Lua Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.
Combien de temps prend la leçon « Objets d’erreur structurés » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon Lua Academy ?
Oui. Chaque leçon Lua Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- La fonction error()
- Appels protégés avec pcall
- xpcall et gestionnaires de messages
- Objets d’erreur structurés