RunesSwap App: Fixing Unhandled Exception Bug

by Alex Johnson 46 views

Encountering bugs is a common part of the software experience, and the RunesSwap app is no exception. This article addresses a specific bug report concerning an unhandled exception that occurs when no valid orders are available. We'll delve into the details of the issue, its causes, and potential solutions. If you've experienced a crash while trying to swap small amounts of sats on RunesSwap, this guide is for you.

Understanding the Unhandled Exception Bug

The Initial Report

The bug was initially reported by a user who encountered a crash while attempting to swap a small amount of sats on the RunesSwap app. The user's environment consisted of Chrome on MacOS, connected with Xverse. The issue arose when the SatsTerminal /quote endpoint returned a 500 error, which the client application failed to handle gracefully, leading to a full crash.

Technical Details: Request and Response

The failing request was a POST request to the https://www.runesswap.app/api/sats-terminal/quote endpoint. The request payload included details such as the Bitcoin amount (btcAmount), the Rune name (runeName), the user's address, and a boolean indicating whether it was a sell order (sell). Here’s a snippet of the request payload:

{
    "btcAmount": "0.00001748",
    "runeName": "LIQUIDIUM•TOKEN",
    "address": "bc1pgn8dswy4myrfkw48qrd05zu2g5sprmvxdryvean553ltzzx0z86qqf8cd3",
    "sell": false
}

The response from the server was a 500 Internal Server Error, with the following JSON payload:

{
    "success": false,
    "error": {
        "message": "No valid orders available for the given parameters",
        "details": "Error: No valid orders available for the given parameters\n    at s.request (/var/task/.next/server/chunks/5883.js:1:5477)\n    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n    at async t.<computed> [as fetchQuote] (/var/task/.next/server/chunks/7618.js:1:5237)\n    at async c.defaultErrorMessage (/var/task/.next/server/app/api/sats-terminal/quote/route.js:1:1245)\n    at async /var/task/.next/server/app/api/popular-runes/route.js:1:630\n    at async to.do (/var/task/node_modules/.pnpm/next@15.2.4_@babel+core@7.27.1_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:18:18605)\n    at async to.handle (/var/task/node_modules/.pnpm/next@15.2.4_@babel+core@7.27.1_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:18:23632)\n    at async eg (/var/task/node_modules/.pnpm/next@15.2.4_@babel+core@7.27.1_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/server.runtime.prod.js:31:28400)\n    at async n2.renderToResponseWithComponentsImpl (/var/task/node_modules/.pnpm/next@15.2.4_@babel+core@7.27.1_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/server.runtime.prod.js:32:1849)\n    at async n2.renderPageComponent (/var/task/node_modules/.pnpm/next@15.2.4_@babel+core@7.27.1_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/server.runtime.prod.js:32:7200)"
    }
}

The error message, "No valid orders available for the given parameters," indicates that the server could not find any matching orders for the requested transaction. This can occur for various reasons, such as insufficient liquidity or unsupported trading pairs.

Front-End Impact

On the front-end, the UI crashed due to an unhandled exception originating from the fetchQuote function. The console logs revealed a FetchError with an HTTP 500 status, followed by an API error in fetchQuote. The SwapProcess reported a FETCH_QUOTE_ERROR, indicating that the quote service was temporarily unavailable. This ultimately led to a React error, specifically "Minified React error #185," which points to an issue in how the application handles errors during the rendering process. This error often arises when a component expects certain data that is not available due to a failed API call.

Diving Deeper: Causes and Solutions

Root Causes

  1. Insufficient Liquidity: The primary cause of the error is the lack of available orders for the specified parameters. This can happen if there aren't enough traders willing to sell the requested runeName for the given btcAmount. Liquidity is crucial for any decentralized exchange (DEX), and low liquidity can lead to failed transactions.
  2. Server-Side Errors: Another contributing factor is the server-side error (HTTP 500). This indicates that the server encountered an issue while processing the request. The error details suggest that the server could not find valid orders, but the underlying cause might be more complex, such as database issues, network problems, or algorithmic errors in the order-matching process.
  3. Unhandled Exceptions: The most critical issue is the unhandled exception on the client-side. While a 500 error from the server is problematic, a well-designed application should gracefully handle such errors. In this case, the client-side code crashed instead of displaying a user-friendly error message or attempting a retry.

Potential Solutions

  1. Improved Error Handling: The most immediate solution is to enhance the client-side error handling. The application should catch the FETCH_QUOTE_ERROR and display a meaningful message to the user. For instance, instead of crashing, the app could show a message like, "No suitable orders found. Please try a different amount or rune," or “The quote service is temporarily unavailable. Please try again later.” This prevents the abrupt crash and provides guidance to the user.
  2. Retry Mechanism: Implementing a retry mechanism can help in cases where the server error is transient. The application could automatically retry the request after a short delay, or prompt the user to retry. This can be particularly effective for intermittent network issues or temporary server overloads.
  3. Liquidity Alerts: The application could provide alerts or warnings when liquidity is low for a particular trading pair. This informs users about potential issues before they attempt a swap, reducing frustration and failed transactions.
  4. Server-Side Improvements: On the server side, it’s essential to investigate the root cause of the "No valid orders available" error. This might involve:
    • Database Optimization: Ensuring the order book database is performing efficiently.
    • Order Matching Algorithm: Reviewing the order-matching algorithm for potential issues or inefficiencies.
    • Monitoring and Logging: Implementing robust monitoring and logging to quickly identify and address server-side issues.
  5. User Input Validation: Validating user inputs can prevent some common issues. For example, the application could check if the btcAmount is within a reasonable range and provide feedback if it's too low or too high.

Practical Steps to Resolve the Issue

For Developers

  1. Implement Error Boundaries: In React, error boundaries are components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of crashing the whole application. Here’s a basic example:
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // You can also log the error to an error reporting service
    console.error("Caught an error: ", error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // You could render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}

// Usage
<ErrorBoundary>
  <YourComponentThatMightError />
</ErrorBoundary>
  1. Enhance API Error Handling: Modify the fetchQuote function to handle errors more gracefully. Use try...catch blocks to catch fetch errors and dispatch appropriate actions to update the UI.
async function fetchQuote(params) {
  try {
    const response = await fetch('/api/sats-terminal/quote', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(params),
    });

    if (!response.ok) {
      const errorData = await response.json();
      throw new Error(errorData.error.message || 'Failed to fetch quote');
    }

    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Fetch error:', error);
    // Dispatch an action to update the UI with an error message
    // Example: dispatch({ type: 'FETCH_QUOTE_ERROR', error: error.message });
    throw error; // Re-throw the error so that upper level error boundaries can catch it
  }
}
  1. Implement User Feedback: Display user-friendly error messages when a quote cannot be fetched. This helps users understand what went wrong and how to proceed.
function SwapComponent() {
  const [quoteError, setQuoteError] = React.useState('');

  const handleSwap = async () => {
    try {
      // Fetch quote logic
    } catch (error) {
      setQuoteError(error.message);
    }
  };

  return (
    <div>
      {quoteError && <div className="error-message">Error: {quoteError}</div>}
      {/* Swap form and other UI elements */}
    </div>
  );
}

For Users

  1. Check Trading Pair Availability: Before attempting a swap, ensure that there is sufficient liquidity for the trading pair you are using. If liquidity is low, consider swapping a larger amount or choosing a different trading pair.
  2. Verify Network Connection: Ensure that your internet connection is stable. Network issues can sometimes lead to failed API requests.
  3. Try Again Later: If you encounter an error, wait a few minutes and try again. The issue might be temporary, such as a server overload or network glitch.
  4. Report the Issue: If the problem persists, report the issue to the RunesSwap support team. Providing detailed information, such as the steps you took and any error messages you encountered, can help them diagnose and fix the problem more quickly.

Conclusion

The unhandled exception bug on RunesSwap when no valid orders are available highlights the importance of robust error handling and client-side resilience. By implementing better error handling, retry mechanisms, and server-side improvements, the RunesSwap app can provide a smoother and more reliable trading experience. For users, understanding the potential causes and taking simple steps like checking liquidity and network connections can help mitigate these issues. Addressing this bug not only improves the app's usability but also enhances user trust and confidence in the platform.

For more information on error handling in React applications, you can visit the React documentation on Error Boundaries.