Vix.cpp docs moved to vixcpp.com
Skip to content

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 ​

js
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().

js
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:

txt
code    : not_found
message : key not found

isOk() and isErr() ​

Use isOk() when you want to handle success:

js
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):

js
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:

js
// 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:

js
console.error(result.error().message);

Use codeString() for logic:

js
if (result.isErr() && result.error().codeString() === "not_found") {
  await client.put("settings/theme", "light");
}

Store errors ​

Key not found ​

js
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 ​

js
const result = await client.put("", "value");

if (result.isErr()) {
  console.error(result.error().message);
}

Client lifecycle errors ​

Client not open ​

js
// 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:

js
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 ​

sh
mkdir -p data

Invalid WAL path ​

js
// 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 ​

js
const state = await client.syncStateInfo();

if (state.isErr()) {
  console.error(`failed to read sync state: ${state.error().message}`);
}

Tick error ​

js
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 ​

js
await client.put("draft/1", "hello");
const tick = await client.tick();

// Even if sync fails:
const value = await client.get("draft/1"); // still readable locally
txt
store failure -> local data problem
sync failure  -> propagation problem

Transport errors ​

Transport disabled ​

js
// wrong
options.enableTransport = false;
await client.startTransport();

// correct
options.enableTransport = true;
options.transportHost = "127.0.0.1";
options.transportPort = 4041;

Port already in use ​

sh
ss -ltnp | grep 4041

Use another port: options.transportPort = 4043.

Peer unavailable ​

js
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 ​

js
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 ​

js
// 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 ​

js
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:

sh
mkdir -p data

Expected output style:

txt
value: Softadastra
missing key handled: not_found
outbox: 1

Errors and local-first behavior ​

Failures should be isolated:

txt
transport failure  -> sync delivery delayed
discovery failure  -> peer finding delayed
sync failure       -> propagation failed
store failure      -> local state operation failed

A transport error should not erase local data. A discovery error should not make the store unusable.

Error API reference ​

Method or fieldPurpose
isOk()Check if the result succeeded
isErr()Check if the result failed
value()Access the success value
error()Access the error value
messageHuman-readable error message
codeString()Error code string
js
const result = await operation();

if (result.isErr()) {
  console.error(result.error().message);
  process.exit(1);
}

const value = result.value();

For operations after client.open():

js
const result = await operation();

if (result.isErr()) {
  console.error(result.error().message);
  await client.close();
  process.exit(1);
}

Common mistakes ​

Reading value() without checking ​

js
// 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() ​

js
// 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:

Go to Examples

Released under the Apache License 2.0.