Skip to main content

Error types

ErrorWhen
TokenParseErrorToken is malformed or signature is invalid
TokenExpiredErrorToken expires_at is in the past
HttpErrorServer returned a non-2xx status
NetworkErrorFetch failed (DNS, timeout, no connection)
CodecErrormsgpack decode or schema validation failed
TransportErrorWebSocket error

Server-side validation errors

Some ops are rejected by the server with 400 before being applied. These surface as HttpError (REST) or ServerMsg::Error { code: 400 } (WebSocket):
RejectionAffected CRDTsDetail
Clock drift > 30sLwwRegister, PresenceClient HLC timestamp is more than 30 seconds ahead or behind server time. Sync your system clock.
Element depth > 1ORSetElement contains nested objects/arrays. Flatten your data structure.
WAL write failureAllStorage is full or unavailable. Server returns 503.

Handling errors

All errors are Data.TaggedError from Effect, matchable with Effect.catchTag:
import { Effect } from "effect";
import { MeridianClient } from "meridian-sdk";

await Effect.runPromise(
  MeridianClient.create(config).pipe(
    Effect.catchTag("TokenExpiredError", (e) =>
      Effect.die(`Token expired at ${new Date(e.expiredAt).toISOString()}`)
    ),
    Effect.catchTag("TokenParseError", () =>
      Effect.die("Invalid token — check MERIDIAN_TOKEN")
    ),
    Effect.flatMap((client) =>
      client.http.getCrdt("my-room", "gc:views").pipe(
        Effect.catchTag("HttpError", (e) =>
          Effect.succeed(`Server error ${e.status}: ${e.body.message}`)
        ),
        Effect.catchTag("NetworkError", (e) =>
          Effect.succeed(`Network error: ${e.message}`)
        ),
      )
    ),
  )
);

Error fields

TokenParseError   → { message: string }
TokenExpiredError → { expiredAt: number }  // ms timestamp
HttpError         → { status: number; body: { message: string } }
NetworkError      → { message: string; cause?: unknown }
CodecError        → { message: string; raw: Uint8Array }
TransportError    → { message: string; cause?: unknown }