Errors
Shiva uses structured errors — not raw error() strings. Every error has a type, a message, and optional context.
Error Types
| Type | Code | When Used |
|---|---|---|
ContractNotBound | E001 | container:make() with no binding |
ValidationError | E002 | Input fails schema validation |
NotFound | E003 | Entity doesn't exist (player, item, etc.) |
Forbidden | E004 | Permission denied |
DatabaseError | E005 | Query failed |
ConfigError | E006 | Invalid or missing config value |
ModuleError | E007 | Generic module-level error |
Throwing Errors
lua
local Err = require('shiva-fw.error')
-- Simple
error(Err.NotFound('Player not found', { playerId = 42 }))
-- Forbidden
error(Err.Forbidden('Insufficient permissions', {
required = 'admin',
got = 'user',
}))Catching Errors
lua
local ok, err = pcall(function()
local player = playerService:get(playerId)
end)
if not ok then
if err.code == 'E003' then
-- handle not found
else
Log.error('Unexpected error', { error = err })
end
endStructured Logging
Shiva errors log automatically with context when unhandled. In dev mode, the full stack trace is included.
[shiva-player] ERROR E003 NotFound: Player not found
context: { playerId = 42 }
at PlayerService.get (server/services/PlayerService.lua:34)Result Type (Optional)
For functions that may fail predictably, use the Result pattern instead of throwing:
lua
local Result = require('shiva-fw.result')
function EconomyService:removeMoney(playerId, account, amount)
local balance = self:getBalance(playerId, account)
if balance < amount then
return Result.fail(Err.Forbidden('Insufficient funds', {
balance = balance,
requested = amount,
}))
end
-- ... do the removal
return Result.ok(true)
end
-- Caller:
local result = economy:removeMoney(source, 'cash', 500)
if not result.ok then
-- result.error is the structured error
notify(source, result.error.message)
end