Skip to content

refactor: standardize HTTP error handling across connectors#564

Merged
fengtality merged 7 commits intodevelopmentfrom
refactor/http-errors
Dec 9, 2025
Merged

refactor: standardize HTTP error handling across connectors#564
fengtality merged 7 commits intodevelopmentfrom
refactor/http-errors

Conversation

@fengtality
Copy link
Contributor

@fengtality fengtality commented Dec 3, 2025

Summary

  • Refactor connector functions to use centralized httpErrors helper instead of fastify.httpErrors
  • Remove fastify parameter from connector route functions where only used for error handling
  • Add comprehensive HttpError class and helper functions in src/services/error-handler.ts
  • Make walletAddress optional for router swap quotes (uses placeholder address)

Changes

New Error Handler Service (src/services/error-handler.ts)

  • HttpError class with proper statusCode and error name mapping
  • Helper functions: badRequest, notFound, internalServerError, serviceUnavailable, forbidden
  • httpErrors object as drop-in replacement for fastify.httpErrors
  • createError function for custom status codes (400, 401, 403, 404, 409, 429, 500, 502, 503)

Connector Refactoring

  • Remove fastify parameter from ~70 connector route functions
  • Replace fastify.httpErrors.* calls with httpErrors.* from error-handler service
  • Standardize error handling patterns across all connectors:
    • Jupiter, Meteora, Orca, Raydium (Solana)
    • Uniswap, PancakeSwap, 0x (Ethereum/EVM)
    • PancakeSwap-Sol (Solana)

Trading Routes Simplification

  • Remove fastify parameter from trading route handlers
  • Simplify src/trading/swap/quote.ts and src/trading/swap/execute.ts
  • Clean up CLMM trading routes

Router Swap Quote Fix

  • Make walletAddress optional for Uniswap/PancakeSwap router quotes
  • Use placeholder address when wallet not provided (allows quotes without wallet)

Test Plan

Automated Tests

  • Run pnpm test - all 91 test suites should pass (774 tests)
  • Run pnpm typecheck - no TypeScript errors

Manual API Tests - Error Responses

400 Bad Request

  • GET /trading/swap/quote?chainNetwork=invalid returns 400 with proper error format
  • POST /connectors/uniswap/clmm/open-position with missing required fields returns 400

404 Not Found

  • GET /trading/swap/quote?chainNetwork=solana-mainnet-beta&baseToken=INVALIDTOKEN&quoteToken=USDC returns 404 for unknown token
  • GET /connectors/raydium/clmm/pool-info?network=mainnet-beta&poolAddress=invalid returns 404

500 Internal Server Error

  • Verify internal errors return proper 500 format with {"statusCode": 500, "error": "HttpError", "message": "..."}

Manual API Tests - Rate Limiting (429)

Repeatedly call these endpoints until 429 Too Many Requests is returned to verify rate limit error handling:

  • GET /chains/solana/balances?network=mainnet-beta&address=<wallet> - repeat until 429
  • GET /chains/ethereum/allowances?network=mainnet&address=<wallet>&tokenSymbols=USDC&spender=<spender> - repeat until 429
  • GET /trading/swap/quote?chainNetwork=solana-mainnet-beta&connector=jupiter/router&baseToken=SOL&quoteToken=USDC&amount=1&side=SELL - repeat until 429

Expected: 429 response returns {"statusCode": 429, "error": "HttpError", "message": "Rate limit exceeded..."}

Manual API Tests - Swap Quotes (without wallet)

Uniswap Router Quote

  • GET /trading/swap/quote?chainNetwork=ethereum-base&connector=uniswap/router&baseToken=WETH&quoteToken=USDC&amount=1&side=SELL returns quote without requiring walletAddress

PancakeSwap Router Quote

  • GET /trading/swap/quote?chainNetwork=ethereum-bsc&connector=pancakeswap/router&baseToken=WBNB&quoteToken=USDC&amount=1&side=SELL returns quote without requiring walletAddress

Manual API Tests - Swap Execution

Solana Swaps

  • Jupiter router swap executes successfully
  • Raydium AMM swap executes successfully
  • Meteora CLMM swap executes successfully

Ethereum Swaps

  • Uniswap router swap executes successfully
  • PancakeSwap router swap executes successfully

Error Format Verification

  • All error responses have consistent format: {"statusCode": <code>, "error": "HttpError", "message": "<message>"}
  • Error messages are descriptive and actionable

🤖 Generated with Claude Code

@fengtality fengtality changed the title Refactor/http errors refactor: standardize HTTP error handling across connectors Dec 3, 2025
@fengtality fengtality marked this pull request as ready for review December 3, 2025 04:40
@rapcmia rapcmia moved this to Under Review in Pull Request Board Dec 3, 2025
@rapcmia rapcmia requested review from nikspz and rapcmia December 3, 2025 09:15
fengtality and others added 3 commits December 3, 2025 09:07
Add httpErrors service for standardized HTTP error responses without
requiring FastifyInstance parameter in connector functions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Replace fastify.httpErrors with centralized httpErrors service
and remove FastifyInstance parameter from connector functions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Update test files to match new function signatures without
FastifyInstance parameter.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@fengtality fengtality force-pushed the refactor/http-errors branch from ff8bfd9 to 09d25be Compare December 3, 2025 17:09
Remove helius and infura namespace references from root.yml since the
rpc-provider-schema.json and rpc/ directory were previously removed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@fengtality fengtality added this to the dev-2.11 milestone Dec 4, 2025
Base automatically changed from fix/cleanups to development December 5, 2025 14:06
@rapcmia
Copy link
Contributor

rapcmia commented Dec 5, 2025

Commit a9b8f7a

  • Setup on Linux on source build
  • Setup this PR with hummingbot (dev) and run successfully on dev and secure mode
  • Added solana and test with default settings including nodeURL
  • Compared all endpoint responses with the corresponding gateway logs.
    • Observed differences between them and evaluated if these variations are relevant or expected 👀

Manual API Tests - Error Responses

  • 400 Bad Request
    • GET /trading/swap/quote?chainNetwork=invalid returns 500 with an unexpected error occured
    • POST /connectors/uniswap/clmm/open-position with missing required fields e.g [] returns each of every parameters until user completes the required ✅
      • This is observed specifically on endpoints that has multiple parameters
    • GET /connectors/jupiter/router/quote-swap with invalid base or quote token returns token not found: invalid
    • GET /connectors/jupiter/router/quote-swap with amount set to string returns must be number
    • POST /connectors/meteora/clmm/add-liquidity with high amount returns Request failed
  • 404 Not Found
    • GET /trading/swap/quote with invalid token returns Token not found ✅
    • GET /connectors/raydium/clmm/pool-info with invalid pool returns pool not found ✅
    • Testing not existing routes or added paramters e.g GET chains/solana/balances?network=mainnet-beta returns 404,
      • Message “Route…. not found” ✅
  • 500 Internal Server Error
    • POST /wallet/add returns message an unexpected error occurred, i think it would be better with message returns related to invalid privatekey specifically for this endpoint? ❗
    • POST /chains/solana/balances with invalid address returns an expected error occurred
    • POST /config/update with incorrect variable type e.g string/integer returns Failed to update configuration: Cannot set server.port to NaN: JSON schema violation
    • POST /connectors/meteora/clmm/open-position with invalid wallet address returns Wallet not found for address
    • POST /connectors/meteora/clmm/collect-fees with recently opened position returns Internal server error

Manual API Tests - Rate Limiting (429)

  • Best to test when using the default nodeURL
  • POST /chains/solana/balances 10x returns Solana RPC rate limit exceeded

Pending

  • Manual API Tests - Swap Quotes (without wallet)
  • Manual API Tests - Swap Execution

incase needed, attach test and gateway logs: 12052025a.zip

@rapcmia
Copy link
Contributor

rapcmia commented Dec 8, 2025

Manual API Tests - Error Responses

400 bad request

  • GET /connectors/jupiter/router/quote-swap with invalid side returned querystring/side must be equal to one of the allowed values but no matching gateway logs ❗
  • POST /chains/solana/balances with invalid network returned body/network must be equal to one of the allowed values but no matching gateway logs ❗

404 not found

  • GET /connectors/meteora/clmm/pool-info with invalid nodeURL returns request failed ✅
  • POST /connectors/meteora/clmm/{add-liquidty, collect-fees} on invalid positionAddress returned Request failed
    • with closed position or empty positionAddress ✅
    • Gateway logs responded with Please provide valid position address
  • POST /config/update with invalid namespace returned Namespace 'ralph-mainnet-beta' not found and logged Namespace 'ralph-mainnet-beta' not found

500 internal server error

  • GET /chains/solana/status?network=mainnet-beta with dead host or invalid nodeURL returns An unexpected error occurred and logs fetch failed (since invalid url) ✅
  • POST /config/update with invalid path for namespace returned Failed to update configuration..JSON schema violation same as gateway logs ❗
    • I think for invalid path for config/update should be under 400 bad requests?

Manual API Tests - Rate Limiting (429)

  • POST /chains/ethereum/allowances until we hit 429 ✅
    • Response ...Rate limit exceeded, retry in 1 minute and gateway logs ..for this request (429 from Gateway rate limiting).

12082025.zip

@nikspz
Copy link
Contributor

nikspz commented Dec 8, 2025

  • commit a9b8f7a
    • Manual APi tests - Swaps quote
      • Uniswap Router Quote
        • GET /connectors/uniswap/router/quote-swap

          • Base

            • returns 500 when field is or Empty / Null ❌

              • got INVALID_ARGUMENT
              2025-12-08 01:18:37 | info |    [UniversalRouter] Output: WETH (0x4200000000000000000000000000000000000006)
              2025-12-08 01:18:37 | info |    [UniversalRouter] Trade type: EXACT_OUTPUT
              2025-12-08 01:18:37 | info |    [UniversalRouter] Recipient: <ethereum-wallet-address>
              2025-12-08 01:18:37 | info |    [UniversalRouter] Slippage: 2%
              2025-12-08 01:18:37 | info |    [UniversalRouter] Protocols to check: V2, V3
              2025-12-08 01:18:37 | info |    [UniversalRouter] Searching for V3 routes...
              2025-12-08 01:18:39 | info |    [UniversalRouter] Found V3 route: 3.159759 -> 0.001
              2025-12-08 01:18:39 | info |    [UniversalRouter] Searching for V2 routes...
              2025-12-08 01:18:39 | info |    [UniversalRouter] Found V2 route: 3.177368 -> 0.001
              2025-12-08 01:18:39 | info |    [UniversalRouter] Found 2 route(s), selecting best route
              2025-12-08 01:18:39 | info |    [UniversalRouter] Creating RouterTrade with V3 route
              2025-12-08 01:18:39 | info |    [UniversalRouter] Building swap parameters...
              2025-12-08 01:18:39 | error |   Error getting quote: invalid address (argument="address", value="<ethereum-wallet-address>", code=INVALID_ARGUMENT, version=address/5.8.0) (argument=null, value="<ethereum-wallet-address>", code=INVALID_ARGUMENT, version=abi/5.8.0) {
                "reason": "invalid address (argument=\"address\", value=\"<ethereum-wallet-address>\", code=INVALID_ARGUMENT, version=address/5.8.0)",
                "code": "INVALID_ARGUMENT",
                "argument": null,
                "value": "<ethereum-wallet-address>"
              }
              • image
              • when walletAddress is 0x08940dc9b5a19fab9319b77c61dda7b8067e6843 ✅

                • returns price successfully
                {
                  "quoteId": "4bf185ab-41a7-4403-843b-49a6897301f3",
                  "tokenIn": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
                  "tokenOut": "0x4200000000000000000000000000000000000006",
                  "amountIn": 9.482452,
                  "amountOut": 0.003,
                  "price": 3160.8173333333334,
                  "priceImpactPct": 0.0102259,
                  "minAmountOut": 0.003,
                  "maxAmountIn": 9.672101040000001,
                  "routePath": "USDC -> WETH"
                }
          • Mainnet

            • walletAddress not added ❌

              • default nodeURL ❌

                • {
                  "statusCode": 500,
                  "error": "HttpError",
                  "message": "invalid address (argument="address", value="", code=INVALID_ARGUMENT, version=address/5.8.0) (argument=null, value="", code=INVALID_ARGUMENT, version=abi/5.8.0)"
                  }

                  2025-12-08 04:42:30 | info |    [UniversalRouter] Trade type: EXACT_OUTPUT                                                                                           2025-12-08 04:42:30 | info |    [UniversalRouter] Recipient: <ethereum-wallet-address>                                                                               2025-12-08 04:42:30 | info |    [UniversalRouter] Slippage: 2%                                                                                                       2025-12-08 04:42:30 | info |    [UniversalRouter] Protocols to check: V2, V3                                                                                         2025-12-08 04:42:30 | info |    [UniversalRouter] Searching for V3 routes...                                                                                         2025-12-08 04:42:38 | info |    [UniversalRouter] Found V3 route: 9.395731 -> 0.003                                                                                  2025-12-08 04:42:38 | info |    [UniversalRouter] Searching for V2 routes...                                                                                         2025-12-08 04:42:52 | info |    [UniversalRouter] Found V2 route: 9.447708 -> 0.003                                                                                  2025-12-08 04:42:52 | info |    [UniversalRouter] Found 2 route(s), selecting best route                                                                             2025-12-08 04:42:52 | info |    [UniversalRouter] Creating RouterTrade with V3 route                                                                                 2025-12-08 04:42:52 | info |    [UniversalRouter] Building swap parameters...                                                                                        2025-12-08 04:42:52 | error |   Error getting quote: invalid address (argument="address", value="<ethereum-wallet-address>", code=INVALID_ARGUMENT, version=address/5.8.0) (argument=null, value="<ethereum-wallet-address>", code=INVALID_ARGUMENT, version=abi/5.8.0) {                                                                   "reason": "invalid address (argument=\"address\", value=\"<ethereum-wallet-address>\", code=INVALID_ARGUMENT, version=address/5.8.0)",                               "code": "INVALID_ARGUMENT",                                                                                                                                          "argument": null,                                                                                                                                                    "value": "<ethereum-wallet-address>"                                                                                                                               }              
              • Changed nodeURL to infura ❌ same error 500 Invalid argument

            • walletAddress 0x08940dc9b5a19fab9319b77c61dda7b8067e6843

              • 200 ✅

                {
                "quoteId":"b3f18a25-f39e-4ba3-a740-26ffa6164cfe",
                "tokenIn":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
                "tokenOut":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
                "amountIn":9.424757,
                "amountOut":0.003,
                "price":3141.5856666666664,
                "priceImpactPct":0.0101061,
                "minAmountOut":0.003,
                "maxAmountIn":9.61325214,
                "routePath":"USDC -> WETH"
                }

        • PancakeSwap Router Quote

          • GET /connectors/pancakeswap/router/quote-swap
            • bsc
              • no wallet used
              • WBNB-USDT
                • Default nodeURL ❌:

                  • {
                    "statusCode": 500,
                    "error": "HttpError",
                    "message": "No routes found for WBNB -> USDT"
                    }
                • changed nodeURL to https://bsc-dataseed4.binance.org✅ 200 success

                  image.png

        • Jupiter GET /connectors/jupiter/router/quote-swap: ok

        • Raydium GET /connectors/raydium/amm/quote-swap: ok

        • Meteora GET /connectors/meteora/clmm/quote-swap: ok

    • Manual API tests swap executions
      • Uniswap

        • base ✅

          • approve spending
          • executes successfully ✅
          {
            "signature": "0x37bc7024b56e00a4793e50451dc6809c71865d065de12d6e57e96b83db5ea441",
            "status": 1,
            "data": {
              "tokenIn": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
              "tokenOut": "0x4200000000000000000000000000000000000006",
              "amountIn": 3.145045,
              "amountOut": 0.001,
              "fee": 1.473252e-7,
              "baseTokenBalanceChange": 0.001,
              "quoteTokenBalanceChange": -3.145045
            }
          }
      • PancakeSwap

        • bsc

          • executes successfully ✅
          {
            "signature": "0x0e1f5ea97bef0502d8810cf888f0331dfbcf45c8b40fbafe5c02c5ac1ff1d4b3",
            "status": 1,
            "data": {
              "tokenIn": "0x55d398326f99059fF775485246999027B3197955",
              "tokenOut": "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",
              "amountIn": 10,
              "amountOut": 0.010992228838725285,
              "fee": 0.0000055918,
              "baseTokenBalanceChange": -10,
              "quoteTokenBalanceChange": 0.010992228838725285
            }
          }
      • Jupiter router swap executes successfully ✅

        {
          "signature": "58cjBRbsrm9MXRf1ciyeQckxmS1qkx7TaS1MfUAxA2rwZTRuxrXRqQ7XoYou8KZ4FKSAGHd9vAqxe17SQ23CNg3X",
          "status": 1,
          "data": {
            "tokenIn": "So11111111111111111111111111111111111111112",
            "tokenOut": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
            "amountIn": 0.10001871000000001,
            "amountOut": 13.854346000000003,
            "fee": 0.000018710000000000002,
            "baseTokenBalanceChange": -0.10001871000000001,
            "quoteTokenBalanceChange": 13.854346000000003
          }
        }
      • Raydium AMM swap executes successfully ✅

        {
          "signature": "3jLwu7aTMKcmTNXd9BmUTdhJpuMoJqqTE9njKNU63eRn6w7axNxiCPmuitYuN3bKz1gfFN3RvCgAHKPSdSWTzPmQ",
          "status": 1,
          "data": {
            "tokenIn": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
            "tokenOut": "So11111111111111111111111111111111111111112",
            "amountIn": 13.876891,
            "amountOut": 0.09999200000000001,
            "fee": 0.000008000000000000001,
            "baseTokenBalanceChange": 0.09999200000000001,
            "quoteTokenBalanceChange": -13.876891
          }
        }
      • Meteora CLMM swap executes successfully ✅

        {
          "signature": "2gFSkoreHUN1LhVvU7UqM98RvAiicxsFHkbBGicaiuWPdevtUxZLgayzpFf9P1vb3VwFs1Tsm16mU5C8YJTJ4t6z",
          "status": 1,
          "data": {
            "tokenIn": "So11111111111111111111111111111111111111112",
            "tokenOut": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
            "amountIn": 0.010005775000000001,
            "amountOut": 1.3799799999999998,
            "fee": 0.000006480000000000001,
            "baseTokenBalanceChange": -0.010005775000000001,
            "quoteTokenBalanceChange": 1.3799799999999998
          }
        }

- Add proper httpErrors (400/404) instead of 500 for validation errors
- Add address validation in connector methods (Orca, Raydium, Meteora, PancakeSwap-Sol)
- Add `if (e.statusCode) throw e;` pattern to re-throw HttpErrors in route handlers
- Fix ConfigManagerV2 to throw proper httpErrors for schema violations
- Add validation error logging in app.ts global error handler
- Update tests for new error behavior

Routes fixed:
- All Raydium AMM routes (executeSwap, quoteLiquidity, removeLiquidity, addLiquidity, positionInfo)
- All Raydium CLMM routes (removeLiquidity, addLiquidity, quotePosition, poolInfo, positionInfo, collectFees)
- Meteora CLMM routes (quotePosition, positionInfo, etc.)
- Orca CLMM routes (poolInfo, positionInfo, positionsOwned)

Connector methods updated:
- meteora.ts: getPositionInfoByAddress(), getPositionInfo()
- orca.ts: getPositionInfo()
- raydium.ts: getPositionInfo()
- pancakeswap-sol.ts: getPositionInfo()

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@fengtality
Copy link
Contributor Author

@rapcmia @nikspz The latest commit should address the issues that you found

@rapcmia
Copy link
Contributor

rapcmia commented Dec 9, 2025

Commit a4d8a89

  • Retest all reported endpoints for error handling
  • Compare the results from previous tests

400 Bad Request ✅

  • GET /trading/swap/quote with invalid chainNetwork now returns HttpError with explicit invalid format message (was 500 Internal Server Error).
  • POST /config/update with server.port invalid now 400 HttpError "Cannot set server.port to NaN" (was 500 InternalServerError).
  • POST /connectors/jupiter/router/execute-quote missing quoteId remains 400 validation; now logged with validation warning.
  • POST /connectors/jupiter/router/execute-quote with quoteId now 400 HttpError “Quote not found or expired” (was 400 simulation failure due to bad nodeURL).
  • POST /connectors/meteora/clmm/open-position now 400 transaction simulation failed (was 400/500 encoding/fetch failures).
  • POST /connectors/meteora/clmm/add-liquidity missing positionAddress now 400 validation (clearer messaging).
    • “Position not found: ralphwashere. Please provide a valid position address” (closed or invalid address)
    • “..body must have required property 'positionAddress’”
  • POST /connectors/meteora/clmm/add-liquidity empty positionAddress now 400 HttpError "Invalid position address" (was 400 request failed).
  • POST /config/update solana-mainnet-beta.ralphURL or invalid paths now 400 HttpError (was 500 InternalServerError).
  • POST /config/update ethereum-avalanche.rpcProvider now 400 HttpError (was 500 InternalServerError).
  • POST /chains/solana/balances with network=ralph-beta still 400 validation and added gatewaty logs
  • GET /connectors/jupiter/router/quote-swap side=B still 400 validation with and added gatewaty logs

404 Not Found

  • GET /connectors/raydium/clmm/pool-info invalid now 404 "Pool not found" (was 500 "Failed to fetch pool info").
  • POST /connectors/meteora/clmm/add-liquidity with invalid/closed position now 404 "Position not found" (was 400 "Request failed" due to insufficient balance or 500).
  • POST /connectors/meteora/clmm/collect-fees with invalid/closed/empty position now 404 "Position not found …" with logs (previously mixed 404/500 or no logs).
  • POST /config/update ralph-mainnet-beta still 404 and added gatewaty logs

500 Internal Server Error

  • POST /wallet/add with invalid key now shows TypeError logs (badRequest undefined) instead of missing log entry
  • POST /chains/solana/balances with bad address still 500 but now TypeError (internalServerError undefined) instead of Non-base58 character.

@fengtality fengtality merged commit a301826 into development Dec 9, 2025
5 checks passed
@fengtality fengtality deleted the refactor/http-errors branch December 9, 2025 12:55
@rapcmia rapcmia moved this from Under Review to Development v2.11.0 in Pull Request Board Dec 9, 2025
@rapcmia rapcmia moved this from Development v2.11.0 to Release v2.10.0 in Pull Request Board Dec 17, 2025
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.

3 participants