Skip to content

Error Handling ​

The SDK surfaces separate HTTP, cancellation, validation, socket decode, and transport failure channels. Catch the specific channel you expect.

EngineHttpError ​

Thrown by any HTTP method on SimulatorClient when the server responds with a non-2xx status. The body is parsed against the api-error-response schema before the error is thrown.

ts
import { 
EngineHttpError
,
createSimulatorClient
} from '@my-swu/simulator-client'
const
simulatorClient
=
createSimulatorClient
({
baseUrl
: 'http://127.0.0.1:4000' })
try { // Missing matches demonstrate the non-2xx HTTP error path. await
simulatorClient
.
matches
.
get
('missing-match')
} catch (
error
) {
if (
error
instanceof
EngineHttpError
) {
// Prefer structured fields over parsing the message string.
console
.
warn
(
error
.
status
,
error
.
code
,
error
.
method
,
error
.
url
,
error
.
message
)
} }

Fields:

  • status — HTTP status code.
  • code — engine error code (for example match_not_found).
  • method — HTTP method used for the request.
  • url — fully resolved request URL.
  • payload — parsed JSON body when available.
  • responseText — raw body text when available.
  • message — human-readable message from the engine.

Common deck and format codes:

  • invalid_selection — deck shape or gameplay choice failed validation. The message names the exact rule, such as sideboard size or copy count.
  • unsupported_format — requested match format or player count is schema-valid but not supported by the current runtime.

EngineRequestAbortedError ​

Thrown by any HTTP method on SimulatorClient when the request is cancelled before the server answers — either because the caller's AbortSignal fired or because a timeout elapsed. Nothing was validated and no status was received, so this is its own channel next to EngineHttpError.

ts
import { 
EngineRequestAbortedError
,
createSimulatorClient
} from '@my-swu/simulator-client'
const
simulatorClient
=
createSimulatorClient
({
baseUrl
: 'http://127.0.0.1:4000',
// Client-level default; any call can override it with its own `timeoutMs`.
timeoutMs
: 10_000,
}) const
unmountController
= new
AbortController
()
try { await
simulatorClient
.
matches
.
get
('match_123', {
signal
:
unmountController
.
signal
})
} catch (
error
) {
if (
error
instanceof
EngineRequestAbortedError
) {
// A caller abort is expected teardown; a timeout is worth reporting. if (
error
.
kind
=== 'timeout') {
console
.
warn
('simulator timed out after',
error
.
timeoutMs
,
error
.
url
)
} } }

Fields:

  • kind — 'aborted' when the caller's signal fired, 'timeout' when the per-call or client-level timeout elapsed first.
  • method — HTTP method used for the cancelled request.
  • url — fully resolved request URL.
  • timeoutMs — timeout that applied to the request, when one did.

See Cancellation and timeouts for how the per-call and client-level controls combine.

SdkValidationError ​

Thrown by parseSchema when a payload does not match the generated schema.

A client only raises it when it was created with a validator — boundary validation is off by default, so an SDK client built without validate: createSchemaValidator() never throws this error from its HTTP methods or its session socket. See Runtime validation. Calling parseSchema yourself always validates, whatever the client is configured with.

ts
import { 
SdkValidationError
} from '@my-swu/simulator-client'
try { // With a validator installed, this fails before anything is sent.
matchSession
.
sendCommand
(/* malformed */ {} as never)
} catch (
error
) {
if (
error
instanceof
SdkValidationError
) {
// Details include schema paths that are useful for developer logs.
console
.
warn
(
error
.
schemaName
,
error
.
details
)
} }

Fields:

  • schemaName — the schema the payload failed against.
  • details — list of path + message strings from Ajv.

Socket-side decoding failures ​

Inbound socket payloads never throw. Bad JSON — or, when the client has a validator, a payload that does not match the socket's protocol schema — is emitted on the error event instead:

ts
matchSession
.
on
('error',
errorPayload
=> {
if (
errorPayload
.
code
=== 'sdk_invalid_server_message') {
// Transport-level decode failure. } else { // Engine-side error payload. } })

This keeps the socket listener resilient to transient garbage or schema drift without crashing the app. A message listener is the wrong place to throw from: an exception there escapes into the transport rather than reaching your code, so the SDK routes it to the session's own error channel instead.

Every socket answers the same way ​

Match sessions, lobby sessions, tournament-room sessions, and the public lobby-list subscription share one frame decoder, so the rule above is uniform across all four:

  • Invalid JSON — sdk_invalid_server_message on the session's error event, whatever the client is configured with.
  • A known frame type with a malformed payload — sdk_invalid_server_message when the client was built with a validator. Without one nothing is checked and the frame is trusted as sent.
  • An unknown frame type — ignored, never an error. The server is allowed to grow message variants without breaking clients built against an older contract.

Each session exposes the same pair of surfaces: an error event and an accumulated errors list.

ts
import { 
createSimulatorClient
,
createSchemaValidator
} from '@my-swu/simulator-client'
const
simulatorClient
=
createSimulatorClient
({
baseUrl
: 'http://127.0.0.1:4000',
// Inherited by every socket this client opens, not only match sessions.
validate
:
createSchemaValidator
(),
}) const
lobbySession
=
simulatorClient
.
lobbies
.
createSession
({
lobbyId
: 'lobby_123',
playerToken
: 'player_token_123',
})
lobbySession
.
on
('error',
errorPayload
=> {
if (
errorPayload
.
code
=== 'sdk_invalid_server_message') {
// Same channel, same code, same reason as the match socket above. } })
console
.
log
(
lobbySession
.
errors
.
length
)

lobbies.createSession(), lobbies.subscribeList(), and tournamentRooms.createSession() take the validator from the client that created them; there is no per-session validation option to set. A client left on the default validates nothing on any of them. See Runtime validation.

Transport failures ​

Network failures from fetch propagate as native TypeError (or similar, depending on the runtime). Cancelled requests are the exception: the SDK normalizes them into EngineRequestAbortedError so a caller never has to sniff runtime-specific AbortError shapes. WebSocket connect failures before the handshake completes reject the matchSession.connect() promise with a generic Error and are recorded in matchSession.errors.

Released under the MIT License.