Summary
When a thrown value is not an Error instance, the SDK's error-formatting idiom loses the actual error text and produces the useless string [object Object].
Reproduction
Creating a wallet with a nametag against the testnet aggregator while the API key is rejected (401):
Wallet creation with nametag failed: SphereError: Failed to mint nametag token: Submit failed: [object Object]
at at.registerNametag
at async at.create
at async at.init
Root cause
- The aggregator client (
@unicitylabs/state-transition-sdk) throws JsonRpcNetworkError on a non-OK HTTP response. That class is not a subclass of Error and has no toString() — unlike its sibling JsonRpcDataError. It does carry useful fields: { name, status, message }.
- sphere-sdk catches it with the idiom
error instanceof Error ? error.message : String(error) (e.g. modules/payments/NametagMinter.ts:182). Since error instanceof Error is false, it falls to String(error) -> Object.prototype.toString() -> [object Object]. The real status: 401 and response body are discarded.
This idiom appears in ~50 places across ~25 source files, so every non-Error throwable that reaches one of them is mis-formatted.
Note: the 401 itself is a configuration issue (invalid testnet API key), not an SDK bug — but the bad formatting hides it from developers.
Proposed fix (sphere-sdk side)
- Add a
formatError(err: unknown): string helper in core/errors.ts that extracts name / status / code / message from error-like objects and falls back to a JSON dump.
- Export it from
core/index.ts and the main index.ts.
- Replace all
instanceof Error ? x.message : String(x) call sites with formatError(x).
- Unit tests covering
Error, JsonRpcNetworkError-shaped objects, JsonRpcDataError-shaped objects, strings, plain objects, null/undefined.
Out of scope (upstream)
JsonRpcNetworkError in @unicitylabs/state-transition-sdk should ideally extend Error (or add toString()). Tracked separately; the sphere-sdk fix makes the SDK resilient regardless.
Summary
When a thrown value is not an
Errorinstance, the SDK's error-formatting idiom loses the actual error text and produces the useless string[object Object].Reproduction
Creating a wallet with a nametag against the testnet aggregator while the API key is rejected (
401):Root cause
@unicitylabs/state-transition-sdk) throwsJsonRpcNetworkErroron a non-OK HTTP response. That class is not a subclass ofErrorand has notoString()— unlike its siblingJsonRpcDataError. It does carry useful fields:{ name, status, message }.error instanceof Error ? error.message : String(error)(e.g.modules/payments/NametagMinter.ts:182). Sinceerror instanceof Errorisfalse, it falls toString(error)->Object.prototype.toString()->[object Object]. The realstatus: 401and response body are discarded.This idiom appears in ~50 places across ~25 source files, so every non-
Errorthrowable that reaches one of them is mis-formatted.Note: the
401itself is a configuration issue (invalid testnet API key), not an SDK bug — but the bad formatting hides it from developers.Proposed fix (sphere-sdk side)
formatError(err: unknown): stringhelper incore/errors.tsthat extractsname/status/code/messagefrom error-like objects and falls back to a JSON dump.core/index.tsand the mainindex.ts.instanceof Error ? x.message : String(x)call sites withformatError(x).Error,JsonRpcNetworkError-shaped objects,JsonRpcDataError-shaped objects, strings, plain objects,null/undefined.Out of scope (upstream)
JsonRpcNetworkErrorin@unicitylabs/state-transition-sdkshould ideally extendError(or addtoString()). Tracked separately; the sphere-sdk fix makes the SDK resilient regardless.