Skip to content

SDK drift: Router.sql() throws a plain untyped Error in TypeScript instead of a PmxtError subclass #1795

Description

@realfishsam

Drift

Router.sql() was added in commit d8ca8fb (feat(sdk): add Router.sql() and cross-venue Router.fetchOrderBook(), #1129/#1670). On a failed request, TypeScript's implementation throws a plain, untyped Error for most failure shapes, while Python's implementation always raises a typed PmxtError subclass, matching the rest of the SDK's error contract.

TypeScript SDK

sdks/typescript/pmxt/router.ts:671-703 (async sql(query: string): Promise<SqlResult>):

if (!response.ok) {
    const body = await response.json().catch(() => ({}));
    if (body.error && typeof body.error === 'object') {
        const { fromServerError } = await import('./errors.js');
        throw fromServerError(body.error);
    }
    throw new Error(body.message || body.error || response.statusText);
}
...
} catch (error) {
    if (error instanceof Error) throw error;
    throw new Error(`Failed to sql: ${error}`);
}

fromServerError (which returns a PmxtError subclass) is only reached when body.error is present and is an object. Any other failure — a string error field, a message-only body, a non-JSON body (falls back to {} via .catch(() => ({}))), or a non-2xx response with an unparseable payload — throws a bare Error via body.message || body.error || response.statusText, or via the catch-all Failed to sql: ${error}. router.ts does not import PmxtError at all (verified: only Exchange, ExchangeOptions, logger, and model types are imported at the top of the file), so there is no static-analysis signal at this call site that anything other than Error can escape.

This differs from the rest of Exchange's hosted/generated methods, which route failures through PmxtError-returning helpers consistently.

Python SDK

sdks/python/pmxt/router.py:694-730 (def sql(self, query: str) -> SqlResult):

try:
    response = self._fetch_with_retry(
        lambda: self._api_client.call_api(
            method="POST", url=url, body={"query": query}, header_params=headers,
        )
    )
    response.read()
    raw = json.loads(response.data)
except ApiException as e:
    raise self._parse_api_exception(e) from None

_parse_api_exception (sdks/python/pmxt/client.py:474) always returns a PmxtError subclass, so every failure path for sql() in Python raises a typed, catchable PmxtError.

Expected

Every public SDK method should raise the SDK's own typed error hierarchy (PmxtError and subclasses) on failure in both languages, per the documented contract in /api-reference/errors ("the same except/catch clause works in both hosted and self-hosted modes"). TypeScript's sql() should route all non-ok responses through fromServerError (or wrap the fallback in PmxtError), not a bare Error.

Impact

A caller who writes catch (e) { if (e instanceof PmxtError) ... } around router.sql(...) in TypeScript will miss failures where the hosted /v0/sql endpoint returns an error body without a structured error object (e.g. a plain-text 500, a {"message": "..."} shape, or a non-JSON body) — the same query in Python would raise a catchable PmxtError for the equivalent failure.


Found by automated SDK cross-language drift audit

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions