Skip to content

Conversation

@mvadari
Copy link
Collaborator

@mvadari mvadari commented Oct 28, 2025

High Level Overview of Change

This PR improves faucet error handling.

When the faucet fails in such a way that it does not deliver a JSON response, you get an incredibly opaque error:

SyntaxError: Unexpected token < in JSON at position 0
    at JSON.parse (<anonymous>)
    at parseJSONFromBytes (node:internal/deps/undici/undici:5595:19)
    at successSteps (node:internal/deps/undici/undici:5566:27)
    at fullyReadBody (node:internal/deps/undici/undici:1665:9)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async specConsumeBody (node:internal/deps/undici/undici:5575:7)

This PR changes that so instead of getting that error, we instead get an HTML output:

'Request failed: {"contentType":"text/html","statusCode":502,"body":"<html>\r\n<head><title>502 Bad Gateway</title></head>\r\n<body>\r\n<center><h1>502 Bad Gateway</h1></center>\r\n<hr><center>nginx/1.18.0 (Ubuntu)</center>\r\n</body>\r\n</html>\r\n"'

Context of Change

The WASM Devnet faucet was down and the error was tricky to debug

Type of Change

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

Did you update HISTORY.md?

  • Yes

Test Plan

Tested locally with the WASM Devnet faucet, which is currently down. I couldn't figure out a good way to write a repeatable test, though.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 28, 2025

Walkthrough

This PR improves faucet error handling by refactoring response processing logic in fundWallet.ts. The changes move JSON parsing into the success branch, restructure the processError function to accept only the Response object, introduce an ErrorData interface for consolidated error information, and update corresponding tests to match the new error message format.

Changes

Cohort / File(s) Change Summary
Documentation
packages/xrpl/HISTORY.md
Added changelog entry documenting "Better faucet error handling" under Unreleased > Fixed section.
Error Handling Implementation
packages/xrpl/src/Wallet/fundWallet.ts
Refactored error handling: moved JSON response parsing into success branch, restructured processError to accept only Response object, introduced ErrorData interface for error metadata, added fallback from JSON to text parsing, and consolidated error construction logic.
Test Updates
packages/xrpl/test/faucet/fundWallet.test.ts
Moved api.disconnect() call from catch block to finally block for guaranteed cleanup; updated error message expectation to reflect new error data field ordering (contentType, statusCode, body).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–25 minutes

  • processError function refactor: Verify the Response cloning, JSON/text parsing fallback logic, and ErrorData construction are correct and handle edge cases
  • XRPLFaucetError serialization: Confirm the error object structure matches expected format when serialized
  • Test error expectations: Ensure the updated error message format precisely matches the new implementation output
  • Function signature change: Validate all call sites of processError pass only the Response object

Possibly related PRs

Suggested labels

bug

Suggested reviewers

  • achowdhry-ripple
  • ckeshava
  • Patel-Raj11

Poem

🐰 A faucet once flowed with messy errors,
Now parsed with care by swift-pawed ferrets!
JSON falls back to text with grace,
Error data finds its proper place,
Better handling—hippity-hop! 🌊

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "fix: improve faucet error handling" is directly related to the main changes in the changeset. The raw summary shows modifications across HISTORY.md, fundWallet.ts, and the test file, all focused on improving how faucet error responses are handled—particularly when the faucet returns non-JSON responses. The title is concise, uses clear language, avoids noise, and is specific enough that teammates scanning the history would understand the primary change is about fixing faucet error handling.
Description Check ✅ Passed The PR description comprehensively follows the provided template with all required sections present. It includes a clear high-level overview explaining how the error handling improves, context explaining why the change was made (the WASM Devnet faucet being down), the type of change marked as a bug fix, confirmation that HISTORY.md was updated, and a test plan describing the manual testing performed. While the author acknowledges challenges in writing repeatable automated tests, they have provided concrete examples of the problem and solution, and the raw summary confirms that test files were updated to reflect the new error handling behavior.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch better-faucet-erroring

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.

@mvadari mvadari changed the title improve faucet erroring fix: improve faucet error handling Oct 28, 2025
@mvadari mvadari marked this pull request as ready for review October 28, 2025 21:21
@mvadari mvadari requested a review from Copilot October 28, 2025 21:22
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR improves error handling in the faucet funding functionality by refactoring how error responses are processed when funding wallet requests fail.

  • Moved body parsing into the error handling function to avoid premature consumption of the response stream
  • Added proper error data structure with type safety
  • Enhanced error handling to gracefully fallback to text when JSON parsing fails

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
packages/xrpl/src/Wallet/fundWallet.ts Refactored error handling by moving response body parsing into processError, added ErrorData interface, and implemented fallback to text response on JSON parse failure
packages/xrpl/HISTORY.md Added changelog entry documenting the improved faucet error handling

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


async function processError(response: Response): Promise<never> {
const errorData: ErrorData = {
contentType: response.headers.get('Content-Type') ?? undefined,
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

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

The ?? undefined is redundant since response.headers.get('Content-Type') already returns string | null. When assigned to an optional property (contentType?: string), null will be automatically converted to undefined without the explicit coalescing operator.

Suggested change
contentType: response.headers.get('Content-Type') ?? undefined,
contentType: response.headers.get('Content-Type'),

Copilot uses AI. Check for mistakes.
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: 0

Caution

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

⚠️ Outside diff range comments (1)
packages/xrpl/src/Wallet/fundWallet.ts (1)

160-169: Guard JSON shape to prevent TypeError on malformed success bodies.

Accessing account.classicAddress without checks can throw if the faucet returns unexpected JSON (still 2xx). Use optional chaining so the existing undefined check handles it gracefully.

Apply:

-    const body: unknown = await response.json()
-    // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- It's a FaucetWallet
-    const classicAddress = (body as FaucetWallet).account.classicAddress
+    const body: unknown = await response.json()
+    // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Best‑effort extraction with guards
+    const classicAddress = (body as FaucetWallet | undefined)?.account?.classicAddress
🧹 Nitpick comments (2)
packages/xrpl/test/faucet/fundWallet.test.ts (1)

126-126: Make error assertion less brittle.

Matching the entire serialized string (incl. exact header value and field order) risks flakes. Prefer checking type and key parts (statusCode, error/detail) or parsing the JSON substring from error.message.

Apply one option:

-      expect(error).toEqual(
-        new XRPLFaucetError(
-          'Request failed: {"contentType":"application/json; charset=utf-8","statusCode":400,"body":{"error":"Invalid amount","detail":"Must be an integer"}}',
-        ),
-      )
+      expect(error).toBeInstanceOf(XRPLFaucetError)
+      const msg = (error as XRPLFaucetError).message
+      expect(msg).toContain('"statusCode":400')
+      expect(msg).toContain('"Invalid amount"')
+      expect(msg).toContain('"Must be an integer"')
packages/xrpl/src/Wallet/fundWallet.ts (1)

208-229: Enrich error context; optionally cap huge bodies.

Add url to aid debugging which faucet endpoint failed, and (optionally) truncate very large text bodies to avoid noisy logs while preserving JSON bodies intact.

Apply:

 interface ErrorData {
   body?: unknown
   contentType?: string
   statusCode: number
+  url?: string
 }
 
 async function processError(response: Response): Promise<never> {
   const errorData: ErrorData = {
     contentType: response.headers.get('Content-Type') ?? undefined,
     statusCode: response.status,
+    url: response.url,
   }
   const clone = response.clone()
   try {
     const body: unknown = await response.json()
     errorData.body = body
   } catch {
-    errorData.body = await clone.text()
+    const text = await clone.text()
+    errorData.body = text.length > 2000 ? `${text.slice(0, 2000)}… [truncated]` : text
   }
   return Promise.reject(
     new XRPLFaucetError(`Request failed: ${JSON.stringify(errorData)}`),
   )
 }

Note: If you adopt truncation, update the test to assert key substrings rather than the full string.

📜 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 30a48e3 and bccd860.

📒 Files selected for processing (3)
  • packages/xrpl/HISTORY.md (1 hunks)
  • packages/xrpl/src/Wallet/fundWallet.ts (3 hunks)
  • packages/xrpl/test/faucet/fundWallet.test.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/xrpl/src/Wallet/fundWallet.ts (1)
packages/xrpl/src/errors.ts (1)
  • XRPLFaucetError (158-158)
⏰ 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). (8)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: browser (24.x)
  • GitHub Check: unit (20.x)
  • GitHub Check: unit (22.x)
  • GitHub Check: integration (24.x)
  • GitHub Check: integration (20.x)
  • GitHub Check: integration (22.x)
🔇 Additional comments (3)
packages/xrpl/HISTORY.md (1)

12-12: Changelog entry reads well.

Clear and scoped; matches the PR intent.

packages/xrpl/test/faucet/fundWallet.test.ts (1)

129-131: Finally block for disconnect is correct.

Ensures cleanup on both success and error paths.

packages/xrpl/src/Wallet/fundWallet.ts (1)

170-170: Centralized error handling LGTM.

Routing all non-JSON/!ok responses through processError improves diagnosability.

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.

2 participants