# Errors
_Path: en/lua/core/errors_
## Table of Contents
- Errors
## Content
# Errors
Structured error handling with categorization and retry metadata. Global `errors` table available without require.
## Creating Errors
```lua
-- Simple message (kind defaults to UNKNOWN)
local err = errors.new("something went wrong")
-- With kind
local err = errors.new(errors.NOT_FOUND, "user not found")
-- Full constructor
local err = errors.new({
message = "user not found",
kind = errors.NOT_FOUND,
retryable = false,
details = {user_id = 123}
})
```
## Wrapping Errors
Add context while preserving kind, retryable, and details:
```lua
local data, err = db.query("SELECT * FROM users")
if err then
return nil, errors.wrap(err, "failed to load users")
end
```
## Error Methods
| Method | Returns | Description |
|--------|---------|-------------|
| `err:kind()` | string | Error category |
| `err:message()` | string | Error message |
| `err:retryable()` | boolean/nil | Whether operation can be retried |
| `err:details()` | table/nil | Structured metadata |
| `err:stack()` | string | Lua stack trace |
| `tostring(err)` | string | Full representation |
## Checking Kind
```lua
if errors.is(err, errors.INVALID) then
-- handle invalid input
end
-- Or compare directly
if err:kind() == errors.NOT_FOUND then
-- handle missing resource
end
```
## Error Kinds
| Constant | Use Case |
|----------|----------|
| `errors.NOT_FOUND` | Resource doesn't exist |
| `errors.ALREADY_EXISTS` | Resource already exists |
| `errors.INVALID` | Bad input or arguments |
| `errors.PERMISSION_DENIED` | Access denied |
| `errors.UNAVAILABLE` | Service temporarily down |
| `errors.INTERNAL` | Internal error |
| `errors.CANCELED` | Operation was canceled |
| `errors.CONFLICT` | Resource state conflict |
| `errors.TIMEOUT` | Operation timed out |
| `errors.RATE_LIMITED` | Too many requests |
| `errors.UNKNOWN` | Unspecified error |
## Call Stack
Get structured call stack:
```lua
local stack = errors.call_stack(err)
if stack then
print("Thread:", stack.thread)
for _, frame in ipairs(stack.frames) do
print(frame.source .. ":" .. frame.line, frame.name)
end
end
```
## Retryable Errors
| Typically Retryable | Not Retryable |
|---------------------|---------------|
| `TIMEOUT` | `INVALID` |
| `UNAVAILABLE` | `NOT_FOUND` |
| `RATE_LIMITED` | `PERMISSION_DENIED` |
| | `ALREADY_EXISTS` |
```lua
if err:retryable() then
-- safe to retry
end
```
## Error Details
```lua
local err = errors.new({
message = "validation failed",
kind = errors.INVALID,
details = {
errors = {
{field = "email", message = "invalid format"},
{field = "age", message = "must be positive"}
}
}
})
local details = err:details()
for _, e in ipairs(details.errors) do
print(e.field, e.message)
end
```
## Navigation
Previous: Base (lua/core/base)
Next: Time (lua/core/time)