Skip to content

fix: better error handling in connections#3182

Open
mvadari wants to merge 9 commits intomainfrom
connection-parse-error
Open

fix: better error handling in connections#3182
mvadari wants to merge 9 commits intomainfrom
connection-parse-error

Conversation

@mvadari
Copy link
Collaborator

@mvadari mvadari commented Jan 12, 2026

High Level Overview of Change

This PR does better error handling for certain edge-case errors (such as jsonInvalid) in responses from rippled.

Context of Change

I was working on some tests for rippled (XRPLF/rippled#6206) and discovered that the xrpl.js client hangs when some errors are received.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

Did you update HISTORY.md?

  • Yes

Test Plan

I wasn't able to write a good integration test for some reason but I did write a unit test and test by hand against a standalone node.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 12, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a89a7ad and 2f4bc5e.

📒 Files selected for processing (1)
  • packages/xrpl/HISTORY.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/xrpl/HISTORY.md

Walkthrough

Client error handling updated to parse and route nested JSON error payloads (including jsonInvalid), adjust emission for null-type responses, and add tests plus mock changes to exercise the new error paths. HISTORY updated to document the fix.

Changes

Cohort / File(s) Summary
Changelog
packages/xrpl/HISTORY.md
Added Unreleased entry documenting improved Client error handling for edge-case error responses; noted export line in 4.6.0.
Client connection
packages/xrpl/src/client/connection.ts
Added ESLint directive; when data.type is null emit data.error_message ?? data.error; handle data.type === 'error' with non-null data.value by parsing value and calling requestManager.reject(parsedValue.id, new Error(data.error)).
Tests
packages/xrpl/test/connection.test.ts
Added test "should handle jsonInvalid errors" using AccountInfoRequest; asserts error produced for jsonInvalid responses.
Test mocks
packages/xrpl/test/createMockRippled.ts
When mock response type: 'error' and value exists, parse value, inject request.id into value.id, re-serialize, and return that payload so client maps/rejects the correct request.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

bug

Suggested reviewers

  • ckeshava
  • pdp2121
  • Patel-Raj11

Poem

🐇 I nibbled through a nested string,
found an id and set it free to spring.
Errors now hop home to their nest,
tests applaud — a tidy quest. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: better error handling in connections' clearly and concisely summarizes the main change: improving error handling for edge cases in the client's connection handling.
Description check ✅ Passed The PR description covers all key required sections: high-level overview, context of change with reference to the upstream issue, type of change (bug fix), HISTORY.md update confirmation, and test plan details.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch connection-parse-error

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8c8c667 and 52e8e67.

📒 Files selected for processing (4)
  • packages/xrpl/HISTORY.md
  • packages/xrpl/src/client/connection.ts
  • packages/xrpl/test/connection.test.ts
  • packages/xrpl/test/createMockRippled.ts
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: ckeshava
Repo: XRPLF/xrpl.js PR: 2874
File: packages/xrpl/test/integration/transactions/permissionedDomain.test.ts:25-80
Timestamp: 2025-01-08T02:12:28.489Z
Learning: The rippled C++ implementation (PR #5161) includes comprehensive test coverage for PermissionedDomain (XLS-80d) error cases. The JS SDK tests focus on the happy path since the error cases are already validated at the rippled level, following the principle of not duplicating complex validation testing across SDK implementations.
Learnt from: shawnxie999
Repo: XRPLF/xrpl.js PR: 2661
File: packages/xrpl/test/integration/transactions/mptokenAuthorize.test.ts:29-118
Timestamp: 2024-12-06T19:25:15.376Z
Learning: In the XRPLF/xrpl.js TypeScript client library, when writing tests (e.g., in `packages/xrpl/test/integration/transactions/`), we generally do not need to test rippled server behaviors, because those behaviors are covered by rippled's own integration and unit tests.
📚 Learning: 2024-12-06T19:25:15.376Z
Learnt from: shawnxie999
Repo: XRPLF/xrpl.js PR: 2661
File: packages/xrpl/test/integration/transactions/mptokenAuthorize.test.ts:29-118
Timestamp: 2024-12-06T19:25:15.376Z
Learning: In the XRPLF/xrpl.js TypeScript client library, when writing tests (e.g., in `packages/xrpl/test/integration/transactions/`), we generally do not need to test rippled server behaviors, because those behaviors are covered by rippled's own integration and unit tests.

Applied to files:

  • packages/xrpl/test/createMockRippled.ts
  • packages/xrpl/src/client/connection.ts
  • packages/xrpl/test/connection.test.ts
📚 Learning: 2024-12-06T19:27:11.147Z
Learnt from: shawnxie999
Repo: XRPLF/xrpl.js PR: 2661
File: packages/xrpl/test/integration/transactions/clawback.test.ts:165-178
Timestamp: 2024-12-06T19:27:11.147Z
Learning: In the integration tests for `clawback.test.ts`, it's acceptable to use `ts-expect-error` to bypass type checking when verifying ledger entries, and no additional type safety improvements are needed.

Applied to files:

  • packages/xrpl/test/createMockRippled.ts
  • packages/xrpl/test/connection.test.ts
📚 Learning: 2024-12-06T18:44:55.095Z
Learnt from: shawnxie999
Repo: XRPLF/xrpl.js PR: 2661
File: packages/xrpl/test/models/MPTokenAuthorize.test.ts:60-71
Timestamp: 2024-12-06T18:44:55.095Z
Learning: In the XRPL.js library's TypeScript test file `packages/xrpl/test/models/MPTokenAuthorize.test.ts`, negative test cases for invalid `Account` address format, invalid `Holder` address format, invalid `MPTokenIssuanceID` format, and invalid flag combinations are not necessary.

Applied to files:

  • packages/xrpl/test/connection.test.ts
📚 Learning: 2025-01-08T02:12:28.489Z
Learnt from: ckeshava
Repo: XRPLF/xrpl.js PR: 2874
File: packages/xrpl/test/integration/transactions/permissionedDomain.test.ts:25-80
Timestamp: 2025-01-08T02:12:28.489Z
Learning: The rippled C++ implementation (PR #5161) includes comprehensive test coverage for PermissionedDomain (XLS-80d) error cases. The JS SDK tests focus on the happy path since the error cases are already validated at the rippled level, following the principle of not duplicating complex validation testing across SDK implementations.

Applied to files:

  • packages/xrpl/test/connection.test.ts
📚 Learning: 2025-02-12T23:28:55.377Z
Learnt from: mvadari
Repo: XRPLF/xrpl.js PR: 2895
File: packages/xrpl/test/models/clawback.test.ts:0-0
Timestamp: 2025-02-12T23:28:55.377Z
Learning: The `validate` function in xrpl.js is synchronous and should be tested using `assert.doesNotThrow` rather than async assertions.

Applied to files:

  • packages/xrpl/test/connection.test.ts
🧬 Code graph analysis (2)
packages/xrpl/test/createMockRippled.ts (2)
packages/xrpl/src/client/connection.ts (1)
  • request (291-319)
packages/xrpl/src/client/index.ts (1)
  • request (340-359)
packages/xrpl/test/connection.test.ts (3)
packages/xrpl/src/client/connection.ts (1)
  • request (291-319)
packages/xrpl/src/client/index.ts (1)
  • request (340-359)
packages/xrpl/src/models/methods/index.ts (1)
  • AccountInfoRequest (523-523)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (4)
packages/xrpl/HISTORY.md (1)

7-9: LGTM!

The changelog entry accurately documents the error handling improvement and follows the existing format.

packages/xrpl/test/createMockRippled.ts (1)

24-29: LGTM!

The logic correctly injects the request ID into the error value payload to support the new error handling path in connection.ts. Since this is test utility code with controlled inputs, the implementation is appropriate.

packages/xrpl/src/client/connection.ts (1)

354-354: LGTM!

Good improvement to provide a fallback when error_message is missing, ensuring errors like slowDown display a meaningful message even when error_message is not set.

packages/xrpl/test/connection.test.ts (1)

1033-1050: LGTM!

Good test coverage for the new jsonInvalid error handling path. The test properly validates that the error is correctly propagated with the expected name and message.

Comment on lines +372 to +386
if (data.type === 'error' && data.value != null) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- needed
const parsedValue: Record<string, unknown> = JSON.parse(
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be true
data.value as string,
)
if (parsedValue.id != null) {
this.requestManager.reject(
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be true
parsedValue.id as string | number,
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be true
new Error(data.error as string),
)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Wrap JSON.parse in a try-catch to handle malformed data.value.

If data.value contains invalid JSON, JSON.parse will throw an uncaught exception. While rippled should always send valid JSON, defensive error handling would prevent the client from crashing on unexpected malformed responses.

🛡️ Proposed fix
     if (data.type === 'error' && data.value != null) {
-      // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- needed
-      const parsedValue: Record<string, unknown> = JSON.parse(
-        // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be true
-        data.value as string,
-      )
-      if (parsedValue.id != null) {
-        this.requestManager.reject(
-          // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be true
-          parsedValue.id as string | number,
-          // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be true
-          new Error(data.error as string),
-        )
+      try {
+        // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- needed
+        const parsedValue: Record<string, unknown> = JSON.parse(
+          // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be true
+          data.value as string,
+        )
+        if (parsedValue.id != null) {
+          this.requestManager.reject(
+            // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be true
+            parsedValue.id as string | number,
+            // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be true
+            new Error(data.error as string),
+          )
+        }
+      } catch (error) {
+        if (error instanceof Error) {
+          this.emit('error', 'badMessage', error.message, data)
+        }
       }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (data.type === 'error' && data.value != null) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- needed
const parsedValue: Record<string, unknown> = JSON.parse(
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be true
data.value as string,
)
if (parsedValue.id != null) {
this.requestManager.reject(
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be true
parsedValue.id as string | number,
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be true
new Error(data.error as string),
)
}
}
if (data.type === 'error' && data.value != null) {
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- needed
const parsedValue: Record<string, unknown> = JSON.parse(
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be true
data.value as string,
)
if (parsedValue.id != null) {
this.requestManager.reject(
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be true
parsedValue.id as string | number,
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be true
new Error(data.error as string),
)
}
} catch (error) {
if (error instanceof Error) {
this.emit('error', 'badMessage', error.message, data)
}
}
}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/xrpl/HISTORY.md (1)

5-15: ⚠️ Potential issue | 🟡 Minor

Changelog entry belongs under ## Unreleased, not ## 4.6.0.

The new ### Fixed block was added inside the already-released 4.6.0 section (tagged 2026-02-12), while the ## Unreleased section directly above it is empty. Because this PR is still open, any code it introduces will ship in a future release, not 4.6.0. Misplacing it here will cause the entry to be missed or duplicated when the next version is cut.

📝 Proposed fix
 ## Unreleased
 
+### Fixed
+* Better error handling in the `Client` for edge case errors
+
 ## 4.6.0 (2026-02-12)
 
 ### Added
 * Add `faucetProtocol` (http or https) option to `fundWallet` method. Makes `fundWallet` work with locally running faucet servers.
 * Add `signLoanSetByCounterparty` and `combineLoanSetCounterpartySigners` helper functions to sign and combine LoanSet transactions signed by the counterparty.
 * Add newly added fields to `Loan`, `LoanBroker` and `Vault` ledger objects and lending protocol related transaction types.
 
-### Fixed
-* Better error handling in the `Client` for edge case errors
-
 ## 4.5.0 (2025-12-16)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/xrpl/HISTORY.md` around lines 5 - 15, Move the misplaced changelog
entry "Better error handling in the `Client` for edge case errors" from the
released section `## 4.6.0 (2026-02-12)` into the `## Unreleased` section:
locate the `### Fixed` block currently under `## 4.6.0 (2026-02-12)` in
HISTORY.md and cut that bullet (or the entire `### Fixed` subsection if it's
only the one entry) and paste it under the `## Unreleased` header, preserving
the `### Fixed` heading if needed so the entry appears in the next release
notes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/xrpl/HISTORY.md`:
- Around line 5-15: Move the misplaced changelog entry "Better error handling in
the `Client` for edge case errors" from the released section `## 4.6.0
(2026-02-12)` into the `## Unreleased` section: locate the `### Fixed` block
currently under `## 4.6.0 (2026-02-12)` in HISTORY.md and cut that bullet (or
the entire `### Fixed` subsection if it's only the one entry) and paste it under
the `## Unreleased` header, preserving the `### Fixed` heading if needed so the
entry appears in the next release notes.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b39c219 and a89a7ad.

📒 Files selected for processing (1)
  • packages/xrpl/HISTORY.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant