feat: implement SDK unsupported feature and capability error standard (#284) - #299
Merged
El-swaggerito merged 1 commit intoJul 27, 2026
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #284
Summary
Implements the standard specified in
docs/capability_error_standard.md, addingUnsupportedFeatureErrorandCapabilityMismatchErrorand routing theconfig-gated vault, Soroban and wallet paths through them.
Root cause
ERROR_CODES[VAULT_CONTRACT_NOT_CONFIGURED].developerHint(src/errors/codes.ts:288)tells integrators to "Set SDKConfig.contractId before vault calls". But
resolveContractId(src/soroban/index.ts:51) only readparams.contractIdandprocess.env.VAULT_CONTRACT_ID— never the config, even though all three vaultentry points accept a
Partial<SDKConfig>.It also threw the raw string
MISSING_CONTRACT_ID, absent from the registry, soisKnownErrorCodereturned false anddescribeErrorfell through to"An unexpected error occurred."
fundTestnetAccounthad the identical defect withTESTNET_ONLY. Following the SDK's own documented path produced a generic error.Changes
src/errors/unsupported.ts(new) — both error classes, extendingPocketPayError, carryingmodule,operation,capabilityand asuggestedNextStepread from the registry'sdeveloperHintso guidance cannotdrift.
toJSON()gives a log-safe view.src/errors/capabilities.ts(new) — capability registry with four statuses:supported,config-gated,planned,unsupported, plusassertCapability().src/soroban/index.ts—resolveContractIdresolves params →SDKConfig.contractId→VAULT_CONTRACT_ID→STELLAR_CONTRACT_ID.src/soroban/client-factory.ts— simulation failures use the registry'sSOROBAN_SIMULATION_FAILED; thevecparameter type reports throughUnsupportedFeatureError.src/wallet/index.ts—fundTestnetAccountusesWALLET_TESTNET_ONLY.capability_error_standard.md; updatedconfiguration.md,error-handling.md,soroban-vault.md.Planned versus supported
The registry classifies real cases rather than aspirational ones:
signer.local— supported.signer.remote— planned. TheSignerinterface is deliberately async soremote signers can replace
LocalSigner(src/account/signer.ts:83-86), butnone ships; consumers may implement
Signerthemselves.soroban.param-type.vec— unsupported.ScValTypeadvertisesvec, but theunderlying Stellar SDK rejects it. Verified against
@stellar/stellar-sdk13.3.0:of the 17 declared types exactly one fails (
invalid type: vec). A test assertsthat throw, so this is reclassified if a future SDK adds support.
No status promises a delivery date; a test asserts that no registry or capability
message contains one.
Error codes
SDK_NOT_IMPLEMENTEDandVAULT_CONTRACT_NOT_CONFIGUREDwere declared with fullspecs but had zero call sites; they get their first ones here. One code is added,
WALLET_TESTNET_ONLY, for a network-gated capability that had no registry entry —codes.tsexplicitly allows this ("Add new ones; never edit/recycle existingones"). Nothing is renamed or recycled.
Backwards compatibility
params.contractIdandVAULT_CONTRACT_IDkeep working unchanged.PocketPayError, so existingcatchblocks and theinstanceof PocketPayErrorre-throws in the vault functions are unaffected.MISSING_CONTRACT_ID→VAULT_CONTRACT_NOT_CONFIGUREDandTESTNET_ONLY→WALLET_TESTNET_ONLY. Neither old string was in the registry.Documented in a Migration section. The vault message still contains "contract ID",
so
mapSorobanContractError()and its existing test are unaffected.Implementation note
PocketPayError's constructor ends withObject.setPrototypeOf(this, PocketPayError.prototype), which overwrites theprototype a subclass gets from
new.target. Both subclasses restore their ownprototype after
super(); without it,instanceofon the subclass silentlyreturns false and
namestays'PocketPayError'. Two tests cover this.Tests
34 new tests in
tests/unsupported-feature.test.tscovering both classes, thecapability registry,
describeError(err.code).knownflipping false to true on thevault and wallet paths, each of the four contract-ID resolution sources,
per-operation metadata,
redactErrorstaying safe,instanceof PocketPayErrorpreserved, and the no-overpromising assertion.
tests/fund.test.tsupdated for thenew wallet code.
Verification
lint,check:circular(39 modules, no cycles) andbuildall pass.Full suite: 46 failed · 677 passed · 1 skipped. The 46 failures are pre-existing
on the base commit — verified by running the suite against a clean checkout of
d7403d8, which gives 46 failed · 643 passed · 1 skipped: same failures in thesame five files (
config-validation,destination-validation,types/asset,retry-policy,payments). This branch adds exactly the 34 new passing tests andintroduces no regressions.
Scope
Limited to the capability-gated paths the issue names. A wider grep found ~25
further error codes outside the registry in payments, transactions and utils; those
are validation errors rather than capability errors and are left untouched rather
than widening this PR.