Skip to main content

Partial success

Both walletPositions and poolDetails support partial success. When you query multiple protocols in one request, each protocol resolves independently. Successful results appear in data, failures appear in errors. This means a timeout from one protocol never blocks the response from another.
Partial failure response
{
  "data": {
    "walletPositions": {
      "data": [
        {
          "protocol": "morpho",
          "walletAddress": "0x742d35cc6634c0532925a3b8d4c9db96c4b4d8b6",
          "chainId": 1,
          "vaultV2Positions": [{ "...": "..." }],
          "marketPositions": []
        }
      ],
      "errors": [
        {
          "protocol": "aave",
          "chainId": 1,
          "walletAddress": "0x742d35cc6634c0532925a3b8d4c9db96c4b4d8b6",
          "error": {
            "code": "PROTOCOL_ERROR",
            "message": "Chain not supported by Aave adapter",
            "retryable": false
          }
        }
      ]
    }
  }
}
Always check both data and errors arrays. A successful response can contain both.

Error structure

type ProtocolError {
  protocol: String!       # Protocol that failed (e.g. "aave")
  chainId: Int!           # Chain ID where the query was attempted
  walletAddress: String!  # Wallet address that was queried
  error: ErrorDetail!     # Structured error detail
}

type PoolProtocolError {
  protocol: String!
  chainId: Int!
  poolAddress: String!    # Pool address that was queried
  error: ErrorDetail!
}

type ErrorDetail {
  code: String!           # Machine-readable error code
  message: String!        # Human-readable description
  retryable: Boolean!     # Whether the client should retry
}

Error codes

CodeDescriptionRetryable
NETWORK_ERRORUpstream service is unreachableYes
VALIDATION_ERRORInvalid input parameters (bad address, unsupported chain)No
TIMEOUTRequest exceeded the time limitYes
PROTOCOL_ERRORProtocol adapter returned an errorNo
UNKNOWN_ERRORUnexpected failureYes

Best practices

Check the retryable field before retrying. For retryable errors, use exponential backoff:
async function queryWithRetry(query, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const result = await fetch(endpoint, { method: "POST", body: JSON.stringify({ query }) });
    const json = await result.json();

    const retryableErrors = json.data.walletPositions.errors
      .filter(e => e.error.retryable);

    if (retryableErrors.length === 0) return json;

    // Wait with exponential backoff before retrying failed protocols
    await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt)));
  }
}
Process successful results immediately, even if some protocols failed:
const { data, errors } = result.data.walletPositions;

// Process successful results
for (const position of data) {
  switch (position.__typename) {
    case "MorphoWalletPositions":
      handleMorpho(position);
      break;
    case "AaveWalletPositions":
      handleAave(position);
      break;
    // ...
  }
}

// Log or handle failures
for (const err of errors) {
  console.warn(`${err.protocol} on chain ${err.chainId}: ${err.error.message}`);
}
These errors are not retryable — fix the input before retrying:
  • Invalid wallet address: Must be a valid hex address (0x-prefixed, 42 characters)
  • Unsupported chain: The protocol does not support the requested chain ID. See supported chains
  • Unknown protocol: The protocol identifier is not recognized