Error Types
The SDK uses several error types:ApiError
Thrown when API requests fail:import { ApiError } from "@polymarket/clob-client";
try {
const order = await clobClient.createAndPostOrder({
tokenID: "invalid-token",
price: 0.50,
side: Side.BUY,
size: 100,
});
} catch (error) {
if (error instanceof ApiError) {
console.error("API Error:", error.message);
console.error("Status Code:", error.status);
console.error("Response Data:", error.data);
}
}
Authentication Errors
Thrown when required authentication is missing:import {
L1_AUTH_UNAVAILABLE_ERROR,
L2_AUTH_NOT_AVAILABLE,
BUILDER_AUTH_NOT_AVAILABLE,
BUILDER_AUTH_FAILED
} from "@polymarket/clob-client";
try {
const apiKeys = await clobClient.getApiKeys();
} catch (error) {
if (error === L1_AUTH_UNAVAILABLE_ERROR) {
console.error("Signer is needed to interact with this endpoint!");
} else if (error === L2_AUTH_NOT_AVAILABLE) {
console.error("API Credentials are needed to interact with this endpoint!");
}
}
Validation Errors
Thrown when parameters are invalid:try {
const order = await clobClient.createOrder({
tokenID: "71321045...",
price: 0.523, // Invalid for tick size 0.01
side: Side.BUY,
size: 100,
});
} catch (error) {
if (error instanceof Error) {
// "invalid price (0.523), min: 0.01 - max: 0.99"
console.error(error.message);
}
}
Throw on Error Mode
By default, the client returns error objects. EnablethrowOnError to throw exceptions instead:
const clobClient = new ClobClient(
host,
chainId,
wallet,
creds,
undefined, // signatureType
undefined, // funderAddress
undefined, // geoBlockToken
undefined, // useServerTime
undefined, // builderConfig
undefined, // getSigner
undefined, // retryOnError
undefined, // tickSizeTtlMs
true // throwOnError - enable exception throwing
);
try {
const order = await clobClient.postOrder(signedOrder, OrderType.GTC);
console.log("Order posted successfully");
} catch (error) {
if (error instanceof ApiError) {
console.error("Failed to post order:", error.message);
console.error("Status:", error.status);
}
}
Retry Logic
Enable automatic retries for transient errors:const clobClient = new ClobClient(
host,
chainId,
wallet,
creds,
undefined, // signatureType
undefined, // funderAddress
undefined, // geoBlockToken
undefined, // useServerTime
undefined, // builderConfig
undefined, // getSigner
true // retryOnError - enable automatic retries
);
// POST requests will automatically retry once after 30ms for:
// - Network errors (ECONNABORTED, ENETUNREACH, etc.)
// - 5xx server errors
const order = await clobClient.postOrder(signedOrder, OrderType.GTC);
Manual Retry Logic
Implement custom retry logic for specific operations:async function postOrderWithRetry(
client: ClobClient,
order: SignedOrder,
maxRetries = 3,
delayMs = 1000
) {
for (let i = 0; i < maxRetries; i++) {
try {
return await client.postOrder(order, OrderType.GTC);
} catch (error) {
if (i === maxRetries - 1) throw error;
if (error instanceof ApiError) {
// Retry on 5xx errors or network errors
if (error.status && error.status >= 500) {
console.log(`Retry ${i + 1}/${maxRetries} after ${delayMs}ms`);
await new Promise(resolve => setTimeout(resolve, delayMs));
continue;
}
}
// Don't retry other errors
throw error;
}
}
}
// Usage
try {
const result = await postOrderWithRetry(clobClient, signedOrder);
console.log("Order posted:", result);
} catch (error) {
console.error("Failed after retries:", error);
}
Common Errors
Invalid Price
try {
const order = await clobClient.createOrder({
tokenID: "71321045...",
price: 1.05, // Price must be between 0.0001 and 0.9999
side: Side.BUY,
size: 100,
});
} catch (error) {
if (error instanceof Error && error.message.includes("invalid price")) {
console.error("Price must be between 0.0001 and 0.9999");
console.error("And must be a multiple of the tick size");
}
}
const tickSize = await clobClient.getTickSize(tokenID);
const tickValue = parseFloat(tickSize);
// Round price to nearest tick
const validPrice = Math.round(desiredPrice / tickValue) * tickValue;
const order = await clobClient.createOrder({
tokenID: tokenID,
price: validPrice,
side: Side.BUY,
size: 100,
});
Invalid Tick Size
try {
const order = await clobClient.createOrder(
{
tokenID: "71321045...",
price: 0.501,
side: Side.BUY,
size: 100,
},
{ tickSize: "0.001" } // Too small for this market
);
} catch (error) {
if (error instanceof Error && error.message.includes("invalid tick size")) {
console.error("Specified tick size is smaller than market minimum");
}
}
const tickSize = await clobClient.getTickSize(tokenID);
const order = await clobClient.createOrder(
{
tokenID: tokenID,
price: 0.50,
side: Side.BUY,
size: 100,
},
{ tickSize } // Use market's tick size
);
Insufficient Balance
import { AssetType } from "@polymarket/clob-client";
try {
const order = await clobClient.createAndPostOrder({
tokenID: "71321045...",
price: 0.50,
side: Side.BUY,
size: 10000, // Very large order
});
} catch (error) {
if (error instanceof ApiError && error.message.includes("insufficient")) {
// Check balance
const balance = await clobClient.getBalanceAllowance({
asset_type: AssetType.COLLATERAL,
});
console.error(`Insufficient balance`);
console.error(`Balance: ${balance.balance}`);
console.error(`Allowance: ${balance.allowance}`);
}
}
const balance = await clobClient.getBalanceAllowance({
asset_type: AssetType.COLLATERAL,
});
const availableBalance = parseFloat(balance.balance);
const requiredAmount = size * price;
if (requiredAmount > availableBalance) {
console.error(`Insufficient balance: need ${requiredAmount}, have ${availableBalance}`);
} else {
const order = await clobClient.createAndPostOrder({
tokenID: tokenID,
price: price,
side: Side.BUY,
size: size,
});
}
Authentication Errors
// L1 Authentication (missing signer)
try {
const client = new ClobClient(host, chainId); // No wallet
await client.createApiKey(); // Requires L1 auth
} catch (error) {
if (error === L1_AUTH_UNAVAILABLE_ERROR) {
console.error("Provide a wallet signer:");
console.error("new ClobClient(host, chainId, wallet)");
}
}
// L2 Authentication (missing credentials)
try {
const client = new ClobClient(host, chainId, wallet); // No creds
await client.getOpenOrders(); // Requires L2 auth
} catch (error) {
if (error === L2_AUTH_NOT_AVAILABLE) {
console.error("Provide API credentials:");
console.error("new ClobClient(host, chainId, wallet, creds)");
}
}
Order Not Found
try {
const order = await clobClient.getOrder("non-existent-order-id");
} catch (error) {
if (error instanceof ApiError && error.status === 404) {
console.error("Order not found");
}
}
const openOrders = await clobClient.getOpenOrders();
const orderExists = openOrders.some(o => o.id === orderId);
if (!orderExists) {
console.log("Order may be filled or canceled");
}
Rate Limiting
try {
// Making too many requests
for (let i = 0; i < 1000; i++) {
await clobClient.getOrderBook(tokenID);
}
} catch (error) {
if (error instanceof ApiError && error.status === 429) {
console.error("Rate limit exceeded");
console.error("Wait before retrying");
}
}
import pLimit from "p-limit";
// Limit concurrent requests
const limit = pLimit(5); // Max 5 concurrent requests
const tokenIDs = ["token1", "token2", "token3", /* ... */];
const orderbooks = await Promise.all(
tokenIDs.map(tokenID =>
limit(() => clobClient.getOrderBook(tokenID))
)
);
Validation Before Posting
Validate orders before posting to catch errors early:import { priceValid } from "@polymarket/clob-client";
async function validateAndPostOrder(
client: ClobClient,
userOrder: UserOrder
) {
// 1. Validate tick size
const tickSize = await client.getTickSize(userOrder.tokenID);
const tickValue = parseFloat(tickSize);
if (!priceValid(userOrder.price, tickSize)) {
throw new Error(
`Invalid price ${userOrder.price} for tick size ${tickSize}`
);
}
// 2. Check balance
const balance = await client.getBalanceAllowance({
asset_type: AssetType.COLLATERAL,
});
const required = userOrder.size * userOrder.price;
const available = parseFloat(balance.balance);
if (required > available) {
throw new Error(
`Insufficient balance: need ${required}, have ${available}`
);
}
// 3. Validate price is in range
if (userOrder.price < tickValue || userOrder.price >= 1) {
throw new Error(
`Price must be between ${tickValue} and ${1 - tickValue}`
);
}
// 4. Create and post
return await client.createAndPostOrder(userOrder);
}
// Usage
try {
const result = await validateAndPostOrder(clobClient, {
tokenID: "71321045...",
price: 0.50,
side: Side.BUY,
size: 100,
});
console.log("Order posted successfully:", result);
} catch (error) {
console.error("Validation failed:", error.message);
}
Logging and Debugging
Enable detailed logging for debugging:class DebugClobClient extends ClobClient {
async createAndPostOrder(...args: any[]) {
console.log("[DEBUG] Creating order:", args[0]);
try {
const result = await super.createAndPostOrder(...args);
console.log("[DEBUG] Order posted successfully:", result);
return result;
} catch (error) {
console.error("[DEBUG] Order failed:", error);
if (error instanceof ApiError) {
console.error("[DEBUG] Status:", error.status);
console.error("[DEBUG] Data:", error.data);
}
throw error;
}
}
}
const client = new DebugClobClient(host, chainId, wallet, creds);
Error Handling Patterns
Graceful Degradation
async function getOrderBookSafely(tokenID: string) {
try {
return await clobClient.getOrderBook(tokenID);
} catch (error) {
console.error(`Failed to fetch orderbook for ${tokenID}:`, error);
// Return empty orderbook structure
return {
market: "",
asset_id: tokenID,
bids: [],
asks: [],
timestamp: new Date().toISOString(),
min_order_size: "1",
tick_size: "0.01",
neg_risk: false,
last_trade_price: "0",
hash: "",
};
}
}
Circuit Breaker
class CircuitBreaker {
private failures = 0;
private lastFailure = 0;
private readonly threshold = 5;
private readonly timeout = 60000; // 1 minute
async execute<T>(fn: () => Promise<T>): Promise<T> {
if (this.isOpen()) {
throw new Error("Circuit breaker is open");
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private isOpen(): boolean {
return (
this.failures >= this.threshold &&
Date.now() - this.lastFailure < this.timeout
);
}
private onSuccess() {
this.failures = 0;
}
private onFailure() {
this.failures++;
this.lastFailure = Date.now();
}
}
const breaker = new CircuitBreaker();
try {
const order = await breaker.execute(() =>
clobClient.postOrder(signedOrder, OrderType.GTC)
);
} catch (error) {
console.error("Circuit breaker prevented request:", error);
}
Next Steps
- Review Creating Orders with error handling
- Learn about Managing API Keys authentication
- Check the API Reference for detailed error codes