@vaultsaas/core and how to resolve them.
Provider Authentication Failures
Wrong or missing API key
Wrong or missing API key
VaultProviderError with code PROVIDER_AUTH_FAILED, or VaultConfigError with code INVALID_CONFIGURATION at startup.Cause: The API key passed to the provider adapter is empty, malformed, or does not match the provider environment.Solution: Verify that the correct environment variable is set and that it is not empty or whitespace-only.Expired or rotated keys
Expired or rotated keys
VaultProviderError with code PROVIDER_AUTH_FAILED at charge time (not at construction). The error context will include httpStatus: 401 or httpStatus: 403.Cause: The key was valid when the client was created but has since been revoked or rotated on the provider dashboard.Solution: Rotate the key in your environment and restart the application. The SDK does not cache provider sessions — a fresh key takes effect on the next API call.Environment mismatch (test vs live)
Environment mismatch (test vs live)
PROVIDER_AUTH_FAILED in production (or vice versa).Cause: Stripe test keys (sk_test_...) only work against test mode. Live keys (sk_live_...) only work against live mode. Similarly, dLocal sandbox credentials do not work against the production endpoint.Solution:- Ensure you are loading the correct key for the target environment.
- For dLocal, also set
baseUrltohttps://sandbox.dlocal.comwhen using sandbox credentials:
Webhook Signature Verification Failures
Wrong webhook secret
Wrong webhook secret
WebhookVerificationError with message ending in “signature verification failed.”Cause: The webhookSecret in the adapter config does not match the secret configured on the provider dashboard.Solution: Copy the webhook signing secret from the provider dashboard and pass it in config.secretKey if webhookSecret is not set. Make sure whichever value you use matches what dLocal sends. For Paystack, the adapter falls back to secretKey if webhookSecret is not set.Missing webhook secret in config
Missing webhook secret in config
WebhookVerificationError: Stripe webhook secret is not configured.Cause: The webhookSecret field was not provided in the Stripe adapter config. Stripe verification requires this value.Solution: Add the webhookSecret field to your Stripe provider config:Raw body parsing issues (Express body-parser gotcha)
Raw body parsing issues (Express body-parser gotcha)
WebhookVerificationError — signature verification fails even though the secret is correct.Cause: Express’s express.json() middleware parses the request body into a JavaScript object. When the SDK re-serializes it, the result differs from the original raw bytes, which breaks the HMAC signature.Solution: Capture the raw body before Express parses it. Use the verify callback to stash the raw buffer:Clock skew (Stripe timestamp tolerance)
Clock skew (Stripe timestamp tolerance)
WebhookVerificationError: Stripe webhook timestamp is too old (exceeds 5-minute tolerance). Possible replay attack.Cause: The Stripe adapter enforces a 5-minute tolerance between the webhook timestamp and the server’s current time. If the server clock is skewed, legitimate webhooks will be rejected.Solution:- Synchronize the server clock with NTP.
- If running in Docker or a VM, ensure the host clock is accurate.
- In CI/testing, make sure the test generates a recent timestamp.
Routing Mismatches
No matching rule
No matching rule
VaultRoutingError with code NO_ROUTING_MATCH or a charge that falls through to an unexpected default provider.Cause: The request’s currency, country, payment method, or amount did not match any specific routing rule, and the request was either handled by the default rule or no rule matched at all.Solution: Review your routing rules. Rules are evaluated top-to-bottom and the first match wins. Add more specific rules above the default:result.routing.reason on a successful charge to see which rule was selected and at which index.Missing default rule
Missing default rule
VaultConfigError: Routing configuration must include a default fallback rule. at construction time, or VaultRoutingError: Routing rules must include a default fallback rule. from the Router.Cause: The SDK requires at least one routing rule with match: { default: true }. This ensures every request can be routed even if no specific rule matches.Solution: Add a default rule at the end of your rules array:Provider does not support the currency or payment method
Provider does not support the currency or payment method
INVALID_REQUEST because the currency or payment method is unsupported.Cause: Routing matched a rule, but the target provider does not support the request’s currency or method. For example, routing a pix payment to Stripe, or an NGN charge to dLocal.Solution: Add match constraints so rules only fire for supported combinations. Each adapter exposes a metadata object with supported currencies, countries, and methods. Use them as a reference:Forced provider is excluded
Forced provider is excluded
VaultRoutingError with code ROUTING_PROVIDER_EXCLUDED.Cause: The request specifies both routing.provider and routing.exclude, and the forced provider appears in the exclude list.Solution: Do not exclude a provider that you are explicitly forcing:Idempotency Conflicts
Same key with different payload
Same key with different payload
VaultIdempotencyConflictError with code IDEMPOTENCY_CONFLICT and message “Idempotency key was reused with a different payload.”Cause: A previous request with the same idempotencyKey was recorded with a different payload hash. The SDK hashes the operation type and the full request object to detect mismatches.Solution: Either reuse the exact same request payload for that key, or generate a new idempotency key:TTL expiration
TTL expiration
idempotency.ttlMs:Custom store missing required methods
Custom store missing required methods
VaultConfigError: Idempotency store is missing required methods.Cause: A custom idempotency store was provided but does not implement all four required methods: get, set, delete, clearExpired.Solution: Ensure your store implements the full IdempotencyStore interface:Platform Connector Issues
Connection refused or DNS resolution failure
Connection refused or DNS resolution failure
ECONNREFUSED or ENOTFOUND.Cause: The platform API at platform.baseUrl is unreachable, or the URL is incorrect.Solution:- Verify the
platformApiKeyandplatform.baseUrlvalues. - Check network connectivity and firewall rules from the application host.
- The SDK gracefully falls back to local routing rules when the platform is unreachable, so charges will continue to work.
Timeouts
Timeouts
routing.source is "local" instead of "platform", and you see timeout warnings in logs.Cause: The platform routing request exceeded platform.timeoutMs (default varies). The SDK falls back to local routing silently.Solution: Tune platform.timeoutMs based on network latency to the platform API. Keep it low (e.g., 100-300 ms) to avoid adding latency to the payment critical path:Fallback behavior
Fallback behavior
result.routing.source to monitor whether charges are being routed by the platform ("platform") or locally ("local"):Empty platform API key
Empty platform API key
VaultConfigError: platformApiKey cannot be empty when provided.Cause: platformApiKey was set to an empty or whitespace-only string.Solution: Either provide a valid API key or omit platformApiKey entirely to disable the connector.Common TypeScript Errors
PaymentMethodInput type mismatch
PaymentMethodInput type mismatch
PaymentMethodInput is a discriminated union. When type is "card", the payload must be either { type: 'card', token: string } or { type: 'card', number: string, expMonth: number, expYear: number, cvc: string }. Mixing fields from both variants causes a type error.Solution: Use exactly one variant:Missing required config fields
Missing required config fields
VaultConfig or VaultClient constructor, such as “Property ‘providers’ is missing in type…”Cause: The VaultConfig type requires providers with at least one entry, and each provider requires adapter and config.Solution: Provide all required fields:Amount type confusion
Amount type confusion
amount field uses the smallest currency unit (cents for USD, kobo for NGN). This is a number type in the SDK, not a float.Solution: Always convert to the smallest unit before calling the SDK:Build and Import Issues
ESM vs CJS
ESM vs CJS
ERR_REQUIRE_ESM, SyntaxError: Cannot use import statement in a module, or ReferenceError: exports is not defined.Cause: @vaultsaas/core ships both ESM (.js) and CJS (.cjs) builds via the exports field in package.json. The wrong format can be loaded if your project’s module system configuration conflicts.Solution:- ESM project (
"type": "module"in yourpackage.json): imports resolve todist/index.jsautomatically. No changes needed. - CJS project (no
"type": "module"): imports resolve todist/index.cjsautomatically via therequirecondition. - Mixed project: ensure your bundler respects the
exportsmap. For TypeScript, set"moduleResolution": "Bundler"or"NodeNext"intsconfig.json.
ts-node, set "esm": true in tsconfig.json or use tsx instead:Node version requirements
Node version requirements
SyntaxError on optional chaining, nullish coalescing, or other ES2022+ syntax. Or fetch is not defined.Cause: The SDK targets ES2022 and requires Node.js 20 or later. Node 18 is missing native fetch support in some builds; Node 16 and earlier lack required syntax support.Solution: Upgrade to Node.js 20+:fetch API available in Node.js 18+ (experimental) and Node.js 20+ (stable).TypeScript version
TypeScript version
exports map or parse newer type syntax.Solution: Use TypeScript 5.0 or later and set appropriate tsconfig.json options:Payment-Specific Issues
Card declines
Card declines
VaultProviderError with code CARD_DECLINED. The context object includes declineCode and providerMessage.Cause: The issuing bank declined the card. Common reasons include insufficient funds, incorrect card details, or fraud prevention.Solution: Present a user-friendly error based on the decline code, and ask the customer to try a different card:4000000000000002— generic decline4000000000009995— insufficient funds4000000000009987— do not honor
3DS / authentication required
3DS / authentication required
VaultProviderError with code AUTHENTICATION_REQUIRED, or the PaymentResult has status: 'requires_action'.Cause: The card issuer requires Strong Customer Authentication (SCA / 3D Secure). This is common in Europe and increasingly elsewhere.Solution: When you receive status: 'requires_action', redirect the customer to complete authentication. The exact flow depends on your frontend integration:requires_action by using stripe.confirmCardPayment() on the client side with the PaymentIntent client secret.Fraud suspected
Fraud suspected
VaultProviderError with code FRAUD_SUSPECTED.Cause: The provider’s fraud detection system flagged the transaction.Solution: Do not retry automatically. Route the payment through manual review, or ask the customer to verify their identity:Currency formatting
Currency formatting
"USD", "BRL", "NGN"). Lowercase values are normalized internally by some adapters, but passing non-ISO codes will fail.Solution: Always use uppercase ISO 4217 codes:amount is always in the smallest currency unit:- USD: cents (100 = $1.00)
- BRL: centavos (100 = R$1.00)
- JPY: yen (1 = 1 yen, zero-decimal currency)
- NGN: kobo (100 = 1 NGN)
Rate limiting
Rate limiting
VaultProviderError with code RATE_LIMITED. The error has retriable: true.Cause: The provider returned HTTP 429 or a rate-limit message. Too many requests were sent in a short period.Solution: Implement exponential backoff. The SDK marks this error as retriable:Provider timeouts
Provider timeouts
VaultNetworkError with code PROVIDER_TIMEOUT. The error has retriable: true.Cause: The provider did not respond within the adapter’s timeout window (default: 15 seconds).Solution:- Retry the request (the SDK marks this as retriable).
- If timeouts are frequent, increase the adapter’s
timeoutMs:
- Investigate if the provider is experiencing an outage.
Error Code Quick Reference
| Code | Category | Retriable | Common Cause |
|---|---|---|---|
INVALID_CONFIGURATION | configuration_error | No | Missing or invalid config values |
PROVIDER_NOT_CONFIGURED | configuration_error | No | Provider referenced but not in config |
PROVIDER_AUTH_FAILED | configuration_error | No | Wrong API key or expired credentials |
NO_ROUTING_MATCH | routing_error | No | No rule matched the request |
ROUTING_PROVIDER_EXCLUDED | routing_error | No | Forced provider is in exclude list |
ROUTING_PROVIDER_UNAVAILABLE | routing_error | No | Provider not configured or disabled |
INVALID_REQUEST | invalid_request | No | Bad request fields or provider validation failure |
IDEMPOTENCY_CONFLICT | invalid_request | No | Same key, different payload |
WEBHOOK_SIGNATURE_INVALID | invalid_request | No | Wrong secret or tampered body |
CARD_DECLINED | card_declined | No | Issuer declined the card |
AUTHENTICATION_REQUIRED | authentication_required | No | 3DS or SCA challenge needed |
FRAUD_SUSPECTED | fraud_suspected | No | Provider fraud check flagged transaction |
RATE_LIMITED | rate_limited | Yes | Too many requests to provider |
NETWORK_ERROR | network_error | Yes | Connection refused, DNS failure, etc. |
PROVIDER_TIMEOUT | network_error | Yes | Provider did not respond in time |
PLATFORM_UNREACHABLE | network_error | Yes | VaultSaaS platform API unreachable |
PROVIDER_ERROR | provider_error | Yes | Provider 5xx or transient failure |
PROVIDER_UNKNOWN | unknown | No | Unmapped provider error |
General Debugging Tips
Enable debug logging
Inspect the full error object
VaultError includes code, category, suggestion, docsUrl, retriable, and context. Log all of them:Check routing metadata
result.routing.reason on successful charges to verify the correct rule matched and the expected provider was selected.Use idempotency keys