Skip to content

Error Handling

All slate functions return Result types — they never raise exceptions. Most errors are represented by the DetsError type defined in the slate module. The single exception is slate/set.update_counter, which returns Result(_, set.UpdateCounterError) so it can carry the counter-specific CounterValueNotInteger case.

For a stable machine-readable code or a user-facing message, use slate.error_code and slate.error_message.

Returned by set.lookup when the key does not exist. Bag and duplicate bag tables return Ok([]) instead — see Lookup behavior differences.

import gleam/dynamic/decode
import slate
import slate/set
let assert Ok(table) = set.open("data/users.dets",
key_decoder: decode.string, value_decoder: decode.int)
let assert Error(slate.NotFound) = set.lookup(table, key: "nonexistent")

Returned by set.insert_new when the key exists, and by bag.insert_new when the exact key-value pair already exists. Plain insert never returns this — it overwrites (set) or silently ignores duplicates (bag).

let assert Ok(Nil) = set.insert_new(table, "alice", 42)
let assert Error(slate.KeyAlreadyPresent) = set.insert_new(table, "alice", 99)

Returned when a write is attempted on a table opened with ReadOnly access.

import gleam/dynamic/decode
import slate.{AutoRepair, ReadOnly}
import slate/set
let assert Ok(table) = set.open_with_access(path: "data/users.dets",
repair: AutoRepair, access: ReadOnly,
key_decoder: decode.string, value_decoder: decode.string)
let assert Error(slate.AccessDenied) = set.insert(table, "key", "value")

Returned when opening a DETS file as a different table type than it was created with — for example, opening a set file as a bag.

import gleam/dynamic/decode
import slate
import slate/bag
import slate/set
// Create a set table
let assert Ok(table) = set.open("data/store.dets",
key_decoder: decode.string, value_decoder: decode.string)
let assert Ok(Nil) = set.insert(table, "key", "value")
let assert Ok(Nil) = set.close(table)
// Try to open the same file as a bag — fails
let assert Error(slate.TypeMismatch) = bag.open("data/store.dets",
key_decoder: decode.string, value_decoder: decode.string)

The DETS file could not be found or accessed. Returned by the open* functions when the path is invalid or the file system denies access.

The path exists and is readable, but the file is not a valid DETS file. Use slate.is_dets_file to check before opening if you accept untrusted paths.

The file was not closed cleanly and you opened it with NoRepair. Reopen with AutoRepair or ForceRepair to recover. See Repair policies.

The table is already open with an incompatible configuration (for example, different access mode).

The table handle is no longer valid — typically because close was already called.

A write would push the DETS file past its 2 GB hard limit. See Limitations for context and mitigations.

slate uses a bounded internal pool of DETS table name slots. If too many distinct files are open at once, new opens fail with this error. See Troubleshooting for recovery steps.

The data on disk did not match the decoders provided when opening the table. See DecodeErrors for diagnosis and recovery strategies.

A catch-all for unexpected Erlang-level errors. The wrapped string is for diagnostics only and is not part of slate's stable API contract. Use slate.error_code(err) (which returns "unexpected_error") for programmatic matching, and report a GitHub issue if you encounter one.

update_counter returns Result(Int, set.UpdateCounterError) rather than DetsError. The dedicated type adds one operation-specific case without polluting the shared error type:

import slate
import slate/set
case set.update_counter(table, key: "hits", increment: 1) {
Ok(new_value) -> new_value
Error(set.CounterValueNotInteger) -> 0
Error(set.TableError(slate.NotFound)) -> 0
Error(set.TableError(err)) -> panic as slate.error_message(err)
}

Match on the variants you expect in normal flows; fall through to a generic branch for the rest:

import gleam/io
import slate
import slate/set
case set.lookup(table, key: "config") {
Ok(value) -> io.println("Found: " <> value)
Error(slate.NotFound) -> io.println("Key not found, using default")
Error(other) ->
io.println("[" <> slate.error_code(other) <> "] " <> slate.error_message(other))
}

For paths where you expect success, let assert keeps initialization and test code concise:

let assert Ok(table) = set.open("data/cache.dets",
key_decoder: decode.string, value_decoder: decode.string)
let assert Ok(Nil) = set.insert(table, "key", "value")
ErrorCauseTypical functions
NotFoundKey missing (set tables only)set.lookup, set.update_counter (via TableError)
KeyAlreadyPresentKey (set) or exact pair (bag) already existsinsert_new (set, bag)
AccessDeniedWrite on read-only tableinsert, insert_list, insert_new, delete_*, update_counter
TypeMismatchWrong table type for fileopen, open_with, open_with_access
FileNotFoundFile missing or inaccessibleopen*, is_dets_file
NotADetsFilePath exists but is not a DETS fileopen*, is_dets_file
NeedsRepairFile not closed cleanly, opened with NoRepairopen_with, open_with_access
AlreadyOpenTable open with different configopen*
TableDoesNotExistInvalid table handle (already closed)Most operations
FileSizeLimitExceededWrite would exceed 2 GBWrite operations
TableNamePoolExhaustedToo many tables open at onceopen*
DecodeErrors(_)On-disk data did not match decodersRead operations
UnexpectedError(_)Unexpected Erlang-level errorAny