Repository navigation
SDK drift: Router.sql() throws a plain untyped Error in TypeScript instead of a PmxtError subclass #1795
Description
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