Skip to content

wasm bindings should reject with a structured error (kind/entity/id), not a Display string #25

Description

@fabracht

Summary

The wasm bindings reject with a plain string — the error's Display text — so JavaScript consumers cannot branch on error kind without substring-matching. They should reject with a structured error carrying a stable kind (and entity/id where applicable).

Current behavior

stitch-wasm/src/lib.rs:

fn err<E: std::fmt::Display>(e: E) -> JsValue {
    JsValue::from_str(&e.to_string())
}

Every fallible binding maps its Result error through err, so a rejected promise carries a string primitive, e.g.:

  • Error::Conflict (409) → "conflict for <entity>/<id>"
  • Error::Ownership (403) → "ownership denied for <entity>/<id>"
  • Error::NotFound (404) → "entity not found: <entity>/<id>"

There is no code, kind, or structure. Consumers are forced into fragile matching such as String(err).startsWith("conflict for") or err.message.includes("conflict"), which breaks silently if the Display wording ever changes.

Motivation

The Store::create conflict-rollback fix (stitch-sync 0.5.0) makes create reject on a unique-key loss so callers can implement exclusive claims (e.g. seat holds). But to detect the loss, the caller must string-match the Display text. A structured error lets them write if (err.kind === "conflict") and drop the substring heuristic entirely.

Proposal

Reject with a JS object (or a wasm-bindgen error type) exposing at least:

  • kind: a stable discriminant — "conflict" | "ownership" | "notFound" | "timeout" | "connectionClosed" | "sessionInvalid" | "config" | "serde" | "io" | "mqtt" | "mqdb" (mirroring the Error variants / classifier methods is_conflict / is_ownership / …).
  • entity, id where the variant carries them.
  • message: the existing Display string, for logging.

Keep it backward-tolerant if feasible (e.g. the object also stringifies to the current text), or treat it as a breaking change in a minor bump and document the migration.

Notes

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions