oss.sarwagya.wtf

Errors

Every failure is a typed DurableError with a stable code.

Every failure the library throws is a DurableError with a code, the operation that failed, an optional slot, and the underlying error attached as cause.

Codes

CodeMeaningRecovery
UNSUPPORTEDRuntime does not implement IndexedDB.Not recoverable; the library will not work here.
STORAGE_UNAVAILABLEBrowser refused to open IndexedDB (private mode restriction, disk error, blocked by another tab).Try again after user interaction; report honestly.
OPEN_FAILEDThe initial read of a slot failed.Retry open(); if persistent, log the cause.
STATE_INVALIDThe validator rejected the stored value (or a migrated value).Application decides; the library never auto-discards.
MIGRATION_REQUIREDThe stored state version is older than declared and no migration step is provided.Ship the missing migration in the next release.
MIGRATION_FAILEDA migration step threw. Previous committed state is unchanged.Application-defined; retry, prompt, or explicitly reset().
FUTURE_VERSIONStored state is newer than the declared version.Ship a newer build, or explicitly reset().
COMMIT_FAILEDA write transaction aborted for a non-quota reason.Retry; if persistent, log the cause.
QUOTA_EXCEEDEDThe write would exceed the origin's storage quota.Do not retry blind. Show a "storage full" UI.
UNSUPPORTED_VALUEThe value contained a shape v1 does not persist (Date, Blob, Map, class instance, cycle, etc.).Serialize to JSON-compatible shape before writing.
SLOT_DESTROYEDA write was attempted after destroy().Reopen the slot before writing.
SLOT_NAME_INVALIDThe slot name does not match the required grammar.Use a name matching /^[a-z0-9][a-z0-9._-]{0,127}$/.
CONFLICTReserved for future concurrency invariants; not raised by v1.

Shape

{
  name: "DurableError",
  code: "MIGRATION_FAILED",
  operation: "migrate",
  slot: "default/workspace",
  message: "Migration 1 → 2 threw. The previous committed value is unchanged.",
  cause: OriginalError,
}

Application state is never placed inside message. If your logging pipeline captures error messages, they will not accidentally spill stored values.

Discriminating errors

import { isDurableError } from "@0xsarwagya/durable-local";
 
try {
  await workspace.set(next);
} catch (err) {
  if (isDurableError(err)) {
    switch (err.code) {
      case "QUOTA_EXCEEDED":
        showStorageFullUi();
        break;
      case "UNSUPPORTED_VALUE":
        // programmer error; log and fix
        break;
      default:
        reportUnexpected(err);
    }
  } else {
    throw err;
  }
}