Errors
Errors in the Softadastra JavaScript SDK are explicit.
The SDK does not assume that an operation succeeded. Most operations return a Result, and the application must check whether the result is successful or failed.
The core rule is:
Check the result before using the value.
Why explicit errors matter
Softadastra is designed for local-first and offline-first systems. In this kind of system, failures are normal: the WAL path may be invalid, the data directory may not exist, a key may be missing, transport may fail, a peer may be unavailable, discovery may find no peers, sync may have failed work, and metadata may be unavailable.
Basic pattern
const result = await client.get("app/name");
if (result.isErr()) {
console.error(result.error().message);
await client.close();
process.exit(1);
}
console.log(result.value().toString());Result
Result represents success or failure.
Common methods: isOk(), isErr(), value(), error().
SoftadastraError
SoftadastraError describes what failed.
Common fields and methods: message, codeString().
const result = await client.get("missing/key");
if (result.isErr()) {
console.log(`code : ${result.error().codeString()}`);
console.log(`message : ${result.error().message}`);
}Expected output style:
code : not_found
message : key not foundisOk() and isErr()
Use isOk() when you want to handle success:
const result = await client.get("app/name");
if (result.isOk()) {
console.log(result.value().toString());
}Use isErr() when you want to handle failure first (the recommended pattern):
const result = await client.get("app/name");
if (result.isErr()) {
console.error(result.error().message);
await client.close();
process.exit(1);
}
console.log(result.value().toString());value() and error()
Only call value() after checking that the result is successful:
// correct
if (result.isOk()) {
const value = result.value();
}
// wrong — assumes key exists and operation succeeded
const value = (await client.get("app/name")).value();message and codeString()
Use message for logs and user-facing diagnostics:
console.error(result.error().message);Use codeString() for logic:
if (result.isErr() && result.error().codeString() === "not_found") {
await client.put("settings/theme", "light");
}Store errors
Key not found
const result = await client.get("missing/key");
if (result.isErr()) {
console.log(result.error().codeString()); // not_found
}A missing key is a normal store error. It should not crash the application.
Invalid key
const result = await client.put("", "value");
if (result.isErr()) {
console.error(result.error().message);
}Client lifecycle errors
Client not open
// wrong
const client = new Client(options);
const result = await client.put("app/name", "Softadastra");
// correct
const opened = await client.open();
if (opened.isErr()) {
console.error(`failed to open client: ${opened.error().message}`);
process.exit(1);
}
const result = await client.put("app/name", "Softadastra");Cleanup after errors
After the client is open, call await client.close() before returning:
const result = await client.put("app/name", "Softadastra");
if (result.isErr()) {
console.error(result.error().message);
await client.close();
process.exit(1);
}
await client.close();WAL errors
Common causes: data directory does not exist, permission denied, invalid WAL path, disk full, write failed, flush failed.
Missing WAL directory
mkdir -p dataInvalid WAL path
// wrong
options.enableWal = true;
options.walPath = "";
// correct
options.enableWal = true;
options.walPath = "data/app.wal";WAL write failed
If WAL append fails, the operation should not be treated as durably accepted.
Sync errors
Read sync state error
const state = await client.syncStateInfo();
if (state.isErr()) {
console.error(`failed to read sync state: ${state.error().message}`);
}Tick error
const tick = await client.tick();
if (tick.isErr()) {
console.error(`failed to tick sync pipeline: ${tick.error().message}`);
}Failed sync work is not lost local data
await client.put("draft/1", "hello");
const tick = await client.tick();
// Even if sync fails:
const value = await client.get("draft/1"); // still readable locallystore failure -> local data problem
sync failure -> propagation problemTransport errors
Transport disabled
// wrong
options.enableTransport = false;
await client.startTransport();
// correct
options.enableTransport = true;
options.transportHost = "127.0.0.1";
options.transportPort = 4041;Port already in use
ss -ltnp | grep 4041Use another port: options.transportPort = 4043.
Peer unavailable
const connected = await client.connect(peer);
if (connected.isErr()) {
console.log(`peer connection failed: ${connected.error().message}`);
}
// Local writes still work:
await client.put("local/key", "value");Discovery errors
No peers found
const peers = await client.peers();
if (peers.isOk() && peers.value().length === 0) {
console.log("no peer discovered yet");
}This can simply mean only one node is running. Local store operations can still work.
Discovery disabled
// wrong
options.enableDiscovery = false;
await client.startDiscovery();
// correct
options.enableDiscovery = true;
options.discoveryHost = "127.0.0.1";
options.discoveryPort = 5051;
options.discoveryBroadcastHost = "127.0.0.1";
options.discoveryBroadcastPort = 5052;Error handling in full example
import { Client, ClientOptions } from "@softadastra/sdk";
const options = ClientOptions.persistent(
"node-errors",
"data/node-errors.wal",
);
options.autoFlush = true;
options.enableTransport = false;
options.enableDiscovery = false;
const client = new Client(options);
const opened = await client.open();
if (opened.isErr()) {
console.error(`open failed: ${opened.error().message}`);
process.exit(1);
}
const written = await client.put("app/name", "Softadastra");
if (written.isErr()) {
console.error(`put failed: ${written.error().message}`);
await client.close();
process.exit(1);
}
const value = await client.get("app/name");
if (value.isErr()) {
console.error(`get failed: ${value.error().message}`);
await client.close();
process.exit(1);
}
console.log(`value: ${value.value().toString()}`);
const missing = await client.get("missing/key");
if (missing.isErr()) {
console.log(`missing key handled: ${missing.error().codeString()}`);
}
const state = await client.syncStateInfo();
if (state.isOk()) {
console.log(`outbox: ${state.value().outboxSize}`);
}
await client.close();Create the data directory first:
mkdir -p dataExpected output style:
value: Softadastra
missing key handled: not_found
outbox: 1Errors and local-first behavior
Failures should be isolated:
transport failure -> sync delivery delayed
discovery failure -> peer finding delayed
sync failure -> propagation failed
store failure -> local state operation failedA transport error should not erase local data. A discovery error should not make the store unusable.
Error API reference
| Method or field | Purpose |
|---|---|
isOk() | Check if the result succeeded |
isErr() | Check if the result failed |
value() | Access the success value |
error() | Access the error value |
message | Human-readable error message |
codeString() | Error code string |
Recommended pattern
const result = await operation();
if (result.isErr()) {
console.error(result.error().message);
process.exit(1);
}
const value = result.value();For operations after client.open():
const result = await operation();
if (result.isErr()) {
console.error(result.error().message);
await client.close();
process.exit(1);
}Common mistakes
Reading value() without checking
// wrong
const value = (await client.get("key")).value();
// correct
const result = await client.get("key");
if (result.isErr()) {
console.error(result.error().message);
await client.close();
process.exit(1);
}
const value = result.value();Ignoring open()
// wrong
const client = new Client(options);
await client.put("key", "value");
// correct
const opened = await client.open();
if (opened.isErr()) {
process.exit(1);
}Treating no peers as a fatal error
No peers can be normal. Local work can continue.
Treating sync failure as local data loss
Sync failure is propagation failure. Local data may still exist.
Returning without cleanup
After the client is open, call await client.close() before returning.
Summary
Errors in the JavaScript SDK are explicit.
The important rules are: check isErr(), use message for humans, use codeString() for logic, do not read value() before checking success, do not treat sync failure as local data loss, do not treat no peers as fatal, and close the client after errors when it was opened.
Next step
Continue with examples: