-
Notifications
You must be signed in to change notification settings - Fork 802
Prevent Unhandled Promise Rejections and Improve Type Safety #7070
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
JFixby
wants to merge
5
commits into
WalletConnect:v2.0
Choose a base branch
from
JFixby:v2.0-fix-unhadled-crashes
base: v2.0
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
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
Fix critical stability issues that cause application crashes: - Add comprehensive error handling for all Promise.all() calls - Prevent unhandled promise rejections in async operations - Add type safety guards for chainId and CAIP parsing - Handle race conditions gracefully (expired proposals/sessions) - Improve error logging (warn vs error for expected conditions) Changes: - engine.ts: Add error handlers to all Promise.all() calls and async ops - engine.ts: Handle missing proposals/sessions in race conditions - engine.ts: Fix onSessionConnect to warn instead of throw - engine.ts: Log error messages instead of full error objects - store.ts: Use warn() for proposals/sessions instead of error() - misc.ts: Enhance createDelayedPromise with proper error handling - caip.ts: Add type check before split() on chain parameter - UniversalProvider.ts: Add type conversion and error handling - eip155.ts: Add type safety checks in getDefaultChain() - caip25.ts: Improve type guards in hexToDecimal/decimalToHex Fixes: - TypeError: chainId.startsWith is not a function - TypeError: chain.split is not a function - Unhandled promise rejections causing crashes - Race conditions with expired proposals/sessions
Contributor
|
All contributors have signed the CTA ✍️ ✅ |
Author
|
I have read the CTA Document and I hereby sign the CTA |
Fix unhandled promise rejections and crashes caused by race conditions in WalletConnect message processing. These changes prevent crashes by gracefully handling expected edge cases. Changes: 1. crypto.decode() - Handle missing symmetric keys gracefully - Wrap generateSharedKey() in try-catch (was outside, causing crashes) - Return undefined on decode failure instead of throwing - Change error logging to warn (expected race condition) 2. history.resolve() - Handle missing history records gracefully - Wrap getRecord() in try-catch to prevent crashes - Log warnings instead of errors for missing records - Continue processing other messages when record not found 3. engine.onRelayMessage() - Handle undefined payload from decode - Check for undefined payload after decode - Early return with warning if decode fails - Prevents downstream crashes from undefined values 4. engine.onRelayEventResponse() - Handle missing history records - Wrap history.get() in try-catch - Early return with warning if record not found - Prevents crashes when response arrives before request recorded These fixes address stack traces showing: - "No matching key. history: <id>" errors - "onRelayMessage() -> failed to process inbound message" errors - Unhandled promise rejections causing crashes All changes maintain backward compatibility and use appropriate log levels (warn for expected race conditions, error for unexpected).
c3afc8a to
84d3c7d
Compare
…d errors Add comprehensive error handling to prevent crashes caused by unhandled promise rejections and missing error handlers in sendResult() and sendError() methods. These changes prevent crashes when history records are missing or when async operations fail. Changes: 1. sendError() - Add catch handler for relayer.publish() promise - Added .catch() handler to relayer.publish() call (line 1926) - Prevents unhandled promise rejection when publish fails - Logs error with proper error message formatting 2. sendError() - Add try-catch for history.resolve() - Wrapped history.resolve() in try-catch block (line 1923-1927) - Prevents crashes when history record is missing during resolve - Logs warning instead of error (expected race condition) 3. sendResult() - Add try-catch for history.resolve() - Wrapped history.resolve() in try-catch block (line 1884-1888) - Prevents crashes when history record is missing during resolve - Logs warning instead of error (expected race condition) 4. sendResult() - Handle missing history records gracefully - Changed history.get() error handling to set record = null instead of throwing (line 1854-1859) - Added null check when accessing record.request.method (line 1864) - Uses default method "wc_sessionRequest" if record is missing - Prevents crashes when history record is cleaned up before response is sent - Changed error logging to warn level (expected race condition) 5. sendError() - Handle missing history records gracefully - Changed history.get() error handling to set record = null instead of throwing (line 1910-1916) - Added null check when accessing record.request.method (line 1922) - Uses default method "wc_sessionRequest" if record is missing - Prevents crashes when history record is cleaned up before error is sent - Changed error logging to warn level (expected race condition) 6. sendResult() - Add null check for record when getting TVF params - Added if (record) check before accessing record.request (line 1846) - Prevents crashes when record is null after history.get() fails These fixes address crashes caused by: - Unhandled promise rejections from relayer.publish() failures - Missing history records when resolving responses/errors - Race conditions where history records are cleaned up before use - Null pointer exceptions when accessing record properties All changes maintain backward compatibility and use appropriate log levels (warn for expected race conditions, error for unexpected failures).
## Changes
### 1. Removed error logging before re-throw (15 locations)
Removed `logger.error()` calls before `throw error` in all catch blocks where errors are re-thrown, since the caller will log them with full context.
**Files changed:**
- `packages/sign-client/src/controllers/engine.ts` (14 locations)
- `providers/universal-provider/src/UniversalProvider.ts` (1 location)
**Locations:**
- `connect() -> pairing.get()` error
- `pair()` error
- `approve() -> proposal.get()` error
- `approve() -> isValidApprove()` error
- `approve()` publish error
- `reject() -> isValidReject()` error
- `reject() -> proposal.get()` error
- `update() -> isValidUpdate()` error
- `extend() -> isValidExtend()` error
- `request() -> isValidRequest()` error
- `request()` Promise.all error
- `ping() -> isValidPing()` error
- `sendRequest() -> core.crypto.encode()` error
- `sendError() -> core.crypto.encode()` error
- `universal-provider request()` error
### 2. Enhanced Promise.all error context
Added detailed context to Promise.all errors to identify which promise failed and what operation was being performed.
**Changes in `engine.ts:request()` method:**
- Wrapped `sendRequest` errors with context: `SendRequest failed for method ${method}, chainId ${chainId}: ${errorMessage}`
- Enhanced Promise.all catch to include: `Promise.all failed in request - method: ${method}, chainId: ${chainId}, error: ${errorMessage}`
- Preserved original error stack trace
**Example error messages:**
- Before: Generic error from Promise.all
- After: `Promise.all failed in request - method: eth_sendTransaction, chainId: eip155:137, error: SendRequest failed for method eth_sendTransaction, chainId eip155:137: Request expired`
## Rationale
Errors that are re-thrown will be caught and logged by the caller with full context. Logging before re-throw creates duplicate logs without adding value. Removing intermediate logging eliminates duplication while preserving error propagation.
Enhanced error context in Promise.all makes it immediately clear which operation failed (sendRequest vs timeout) and what was being attempted (method and chainId), significantly improving debugging experience.
## Impact
- Eliminates duplicate error logs when errors are re-thrown
- Improves error messages with method and chainId context
- Makes debugging easier by clearly identifying failed operations
- No functional changes - error handling behavior unchanged
… unhandled crashes - Add serializeError() method to properly serialize errors of various types (Error instances, objects, primitives) with comprehensive detail capture - Enhance error handling in sendRequest catch block to preserve full error context including type, keys, own properties, and stringified representation - Improve Promise.all error handling with detailed error information capture - Handle empty error objects from wallet responses by providing default error structure with meaningful message and error code - Preserve original error stack traces while adding contextual information This prevents unhandled promise rejections and improves debugging by capturing complete error information before it might be lost.
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.
This PR addresses critical stability issues in WalletConnect v2.0 that cause application crashes due to unhandled promise rejections, type safety errors, and race conditions. The changes add comprehensive error handling, type guards, and graceful degradation for edge cases that occur in production environments.
Problems Addressed
1. Unhandled Promise Rejections
Multiple locations in the codebase had unhandled promise rejections that could crash Node.js applications:
Promise.all()calls without error handlers2. Type Safety Errors
Runtime errors when non-string values are passed to string methods:
TypeError: chain.split is not a functionTypeError: chainId.startsWith is not a functionTypeError: hex.startsWith is not a function3. Race Conditions
Missing proposals/sessions causing crashes when:
4. Error Logging Issues
error()for expected race conditions (expired proposals/sessions)Solution
1. Comprehensive Promise Error Handling
Added
.catch()handlers to allPromise.all()calls and async operations to prevent unhandled rejections:engine.ts2. Type Safety Guards
Added explicit type checking and conversion before calling string methods:
String()conversions for non-string values3. Race Condition Handling
Graceful handling of missing proposals/sessions:
proposal.get()andsession.get()calls4. Improved Logging
warn()instead oferror()for expected race conditionsDetailed Changes
packages/sign-client/src/controllers/engine.ts(282 lines changed)Error Handling Improvements:
processPendingMessageEvents()initializationgetTVFParams()in try-catch to handle synchronous errors (e.g., startsWith errors)Promise.all()calls:onProviderMessageEvent()in error handlerRace Condition Fixes:
onSessionProposeResponse(): Try-catch aroundproposal.get()with warning for missing proposalsonSessionSettleRequest(): Try-catch aroundproposal.get()with warning for missing proposalsonSessionUpdateRequest(): Graceful handling of missing sessions with warningonSessionConnect(): Changed from throwing error to logging warning when no listenersError Logging:
onRelayMessage(): Log error message instead of full error objectPromise Rejection Prevention:
packages/core/src/controllers/store.ts(9 lines changed)Logging Improvements:
warn()instead oferror()for proposals and sessionspackages/utils/src/misc.ts(55 lines changed)Promise Rejection Handling:
createDelayedPromise()timeout handler with comprehensive error handlingpackages/utils/src/caip.ts(4 lines changed)Type Safety:
split()on chain parameterproviders/universal-provider/src/UniversalProvider.ts(25 lines changed)Type Safety:
Error Handling:
Provider Safety:
setDefaultChain()availability before callingproviders/universal-provider/src/providers/eip155.ts(23 lines changed)Type Safety:
getDefaultChain()with type safety checkshandleSwitchChain()before callingstartsWith()providers/universal-provider/src/utils/caip25.ts(42 lines changed)Type Safety:
hexToDecimal()anddecimalToHex()functionshexToDecimal()anddecimalToHex()inextractCapabilitiesFromSession()Files Changed
packages/sign-client/src/controllers/engine.ts(282 lines changed - major refactoring)packages/core/src/controllers/store.ts(9 lines changed)packages/utils/src/misc.ts(55 lines changed)packages/utils/src/caip.ts(4 lines changed)providers/universal-provider/src/UniversalProvider.ts(25 lines changed)providers/universal-provider/src/providers/eip155.ts(23 lines changed)providers/universal-provider/src/utils/caip25.ts(42 lines changed)package-lock.json(64 lines removed - dependency cleanup)Total: 8 files changed, 346 insertions(+), 158 deletions(-)
Testing
These changes have been tested in a production environment with:
Impact
Related Issues
This PR addresses: