Skip to content

Errors

Shiva uses structured errors — not raw error() strings. Every error has a type, a message, and optional context.

Error Types

TypeCodeWhen Used
ContractNotBoundE001container:make() with no binding
ValidationErrorE002Input fails schema validation
NotFoundE003Entity doesn't exist (player, item, etc.)
ForbiddenE004Permission denied
DatabaseErrorE005Query failed
ConfigErrorE006Invalid or missing config value
ModuleErrorE007Generic 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
end

Structured 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

See Also

Released under the MIT License.