This guide is the symptom-first companion to the Migrating Your Polymarket Bot to CLOB V2 guide. If you’re getting errors that the migration guide didn’t predict, this is where to look. It is distinct from our Polymarket API Top 10 Problems, which covers the cross-cutting Global-vs-US architecture issues — this page is specifically about V2 SDK errors devs are hitting right now in production. For auth-only debugging see Polymarket Auth Troubleshooting.

Last verified: May 25, 2026 — against open GitHub issues, the live py_clob_client_v2==1.0.1 source, and the V2 migration docs.


Read this first: there’s an active bug blocking new API users

Before the table: if you’re a new Polymarket developer trying to follow the documented path — deposit wallet + signature_type=POLY_1271 (sig type 3) — every order you place fails with:

HTTP 400 — {"error": "the order signer address has to be the address of the API KEY"}

This is py-clob-client-v2 issue #70 (open as of May 19, 2026, reproduced against py-clob-client-v2==1.0.1 and polymarket_client_sdk_v2==0.5.1). The same root cause is filed for the TypeScript SDK at clob-client-v2 #65. The Rust SDK shows the same symptom.

The bug — verified against the V2 SDK source — is that the L1 authentication path (_l1_headerscreate_level_1_headerssign_clob_auth_message) ignores signature_type and funder. It always signs the auth message as the EOA and registers POLY_ADDRESS = signer.address(). So create_or_derive_api_key() returns credentials bound to the EOA. When you then place an order, the SDK correctly sets signer = deposit_wallet, the CLOB checks “does the order’s signer match the API key’s owner?”, and it doesn’t.

There is no SDK-level workaround that does it correctly today. The practical workaround new users are using:

  1. Sign up via the Polymarket UI with email/Magic Link. This deploys a proxy wallet under the hood. Export the wallet’s private key.
  2. Use signature_type=1 (POLY_PROXY) and pass the proxy wallet’s address as funder.

This works because pre-cutover Magic-wallet accounts have API keys bound to the proxy address through Polymarket’s own onboarding flow, not the SDK’s. The trade-off is one Polymarket account per bot operator and a manual onboard. It’s ugly. It also conflicts with Polymarket’s documentation, which tells new users to use deposit wallets and POLY_1271 — that path is currently inoperable from the SDKs.

If you’re a multi-bot operator: the legacy Safe flow (signature_type=2) also still works if you have a pre-cutover Safe deployed. New Safe deployments hit the same wall via #53 below.

Track #70 and #53 for fix status; we’ll update this page when the SDK ships the ERC-7739-wrapped L1 auth path described in the proposed-fix sketch on #70.


V2 SDK errors — symptom → cause → fix

This is the workhorse table — V2 SDK errors devs are hitting after migrating their package imports to py_clob_client_v2 (or @polymarket/clob-client-v2). Causes are verified against the live V2 client source.

Auth & wallet errors

Symptom (error message)Real causeFix
PolyApiException 400 — {'error': 'the order signer address has to be the address of the API KEY'}The known POLY_1271 L1 auth bug (#70). create_or_derive_api_key() registers your API key against the EOA, but signature_type=3 makes the SDK sign orders with signer = deposit_wallet. Mismatch → reject.No SDK fix as of May 25, 2026. Workaround: use a Magic-wallet account (signature_type=1) + proxy funder. Or hand-craft the ERC-7739-wrapped L1 auth (see the proposed-fix sketch on #70).
PolyApiException 400 — {'error': 'maker address not allowed, please use the deposit wallet flow'}#53. The basic EOA flow (ClobClient(host, chain_id, key) with signature_type=0) is rejected for new wallets post-cutover. Polymarket now requires the deposit-wallet flow for all new programmatic users.Same workaround as above — use a pre-cutover Magic-wallet or Safe account until #70 is fixed. The documented EOA-only quickstart no longer works on mainnet.
PolyException: L1_AUTH_NOT_AVAILABLEThe client has no signer — you constructed it without key=. Raised by assert_level_1_auth() in client.py.Pass key= (your private key) to the ClobClient constructor. L1 methods (creating API keys, signing orders) require a wallet.
PolyException: L2_AUTH_NOT_AVAILABLEThe client has a signer but no API creds. Raised by assert_level_2_auth(). Common on the first script run after generating fresh creds.Run creds = client.create_or_derive_api_key() first, then either build a second client with creds=creds, or call client.set_api_creds(creds) on the existing one.
INVALID_SIGNATURE on every requestsignature_type doesn’t match how your wallet was deployed. 0 (EOA), 1 (POLY_PROXY for Magic), 2 (GNOSIS_SAFE for browser-deployed Safes), 3 (POLY_1271 for deposit wallets) are not interchangeable.Verify the wallet type at polymarket.com/settings. Pre-cutover Safe → sig type 2. Magic / Google login → sig type 1. Fresh EOA → sig type 0 (blocked, see above). Deposit wallet → sig type 3 (currently broken in SDK).
INVALID_SIGNATURE only after some timeClock drift. EIP-712 includes a timestamp; the server rejects more than ~60s of skew.Sync your clock (ntpdate/chrony). Or pass use_server_time=True in the constructor so the SDK queries the CLOB server time before each signed request.

Order-shape errors (mostly migration leftovers)

SymptomReal causeFix
ImportError: cannot import name 'BUY' from 'py_clob_client.order_builder.constants'The constants module was removed. V2 uses a real Side enum.from py_clob_client_v2 import Side — then Side.BUY / Side.SELL.
ImportError: cannot import name 'OrderArgs' from 'py_clob_client.clob_types'You’re still importing from the V1 package.from py_clob_client_v2 import OrderArgs, MarketOrderArgs, OrderType, PartialCreateOrderOptions.
AttributeError: 'ClobClient' object has no attribute 'get_balance'get_balance was removed in V2 — there is no such method on the V2 client.Use client.get_balance_allowance(BalanceAllowanceParams(asset_type=AssetType.COLLATERAL)) for pUSD balance + allowance.
AttributeError: 'ClobClient' object has no attribute 'get_positions'get_positions was also removed. Positions live on the Data API, not the CLOB client.requests.get('https://data-api.polymarket.com/positions', params={'user': wallet_address}).json() — no auth required.
AttributeError: 'dict' object has no attribute 'bids'get_order_book() now returns the raw JSON dict, not the typed OrderBookSummary.Use book["bids"][0]["price"] (dict access), or wrap with from py_clob_client_v2.utilities import parse_raw_orderbook_summary; typed = parse_raw_orderbook_summary(book).
TypeError: OrderArgs.__init__() got an unexpected keyword argument 'fee_rate_bps' (or 'nonce' or 'taker')Those fields were removed from the order struct. Fees are now set by the protocol at match time; uniqueness comes from timestamp (ms), not nonce.Delete those kwargs. If you need fee-aware fills on market buys, pass user_usdc_balance=<float> instead — the SDK will adjust the fill amount to leave room for the fee.
ValueError: post_only is not supported for FOK/FAK ordersThe V2 client raises this explicitly (V1 sometimes ignored silently). Post-only and immediate-execute order types are mutually exclusive by definition.Use post_only=True only with OrderType.GTC. For FOK/FAK, set post_only=False.

Pricing & tick-size errors

SymptomReal causeFix
PolyException: invalid price (0.553), min: 0.01 - max: 0.99Your price isn’t a multiple of the market’s tick size. Most markets are 0.01; some (sports, low-priced) are 0.001.Round to the market’s tick: tick = client.get_tick_size(token_id) then price = round(price / float(tick)) * float(tick). Or read tick from getClobMarketInfo(condition_id) along with other market params.
PolyException: failed to fetch market info for condition id <id>The condition ID isn’t tradeable on the CLOB — usually it’s a Gamma-only event-level ID, not a CLOB market condition.The conditionId you want is on the market (one per outcome pair), not the event. Use clobTokenIds from /markets, or client.get_market_by_token(token_id) to resolve.
PolyException: failed to resolve condition id for token <id>The token_id doesn’t exist or has been delisted (resolved market).Re-verify the token ID against Gamma. Resolved markets have empty books and may not resolve to a condition.
error parsing fee rate bps () to int64 (#11)Backend returns an empty base_fee on certain markets; the SDK’s get_fee_rate_bps tries to cast "" to int.Hit only on a few edge-case markets. Update to the latest py-clob-client-v2 patch (PR pending at time of writing) or wrap the call in a try/except that defaults to 0.

Order-placement & matching errors

SymptomReal causeFix
Market FOK orders failing with "no match" on thin booksFOK requires the entire size to fill immediately. On a 100-share, $5 book your $25 FOK fails — even though FAK would partially fill.Switch to OrderType.FAK (Fill And Kill): fills what’s available immediately, cancels the rest. Or pre-check the book depth before submitting an FOK.
Orders sit on the book but never fill on Neg-Risk marketsYou didn’t set neg_risk=True and the SDK auto-detected wrong — or you set it manually and got it wrong.Let the SDK auto-detect: omit neg_risk from PartialCreateOrderOptions and the V2 client calls get_neg_risk(token_id) automatically. Or read it from client.get_neg_risk(token_id) before placing.
builder_code is required and cannot be zero from get_builder_tradesYou passed BYTES32_ZERO (32 zero bytes) as the builder_code.Use a real builder_code from your Builder Profile, or don’t filter by builder.
invalid user provided fee rate: X, fee rate for the market must be YYou explicitly set fee_rate_bps on an OrderArgs and the SDK is running in V1-protocol fallback (rare). The user value disagrees with the market value.Stop setting fee_rate_bps — it’s not user-settable on V2 orders. The SDK will resolve the correct value.

Backend version & cutover artifacts

SymptomReal causeFix
PolyApiException 400 — {'error': 'order_version_mismatch'}You’re on the legacy py-clob-client (V1) signing V1 orders against the V2 backend. This is exactly the error pattern in py-clob-client #335, #336, #337 — the V1 repo was archived May 11, 2026.Migrate to py-clob-client-v2. The V1 client has no path forward. See Migrating Your Polymarket Bot to CLOB V2.
order_version_mismatch intermittently on a V2 clientThe backend version flipped (rare — usually only during a hot upgrade). The V2 client’s _retry_on_version_update is designed to handle this by re-resolving the version via GET /version and retrying once.Should self-recover. If it persists, manually call client.get_version() and verify the client’s _cached_version is updating.
400 invalid signature only on certain Magic-wallet accounts (py-clob-client #277)EOA-only mode (signature_type=0) works for balance/allowance but the proxy mode (signature_type=1 with the same key) gets rejected. The EOA and proxy addresses are different and the wrong one is being signed.Always pair signature_type=1 with funder = <proxy address>. The proxy address is at polymarket.com/settings, not your EOA.

V1 errors (for people who never migrated)

py-clob-client is archived. It will keep installing forever, but it cannot sign valid V2 orders. The errors below are what you get if you missed the cutover. The only fix for any of these is to migrate.

SymptomCauseFix
{'error': 'order_version_mismatch'} on every post_orderV1 signed orders use EIP-712 Exchange domain version "1"; V2 requires "2". The CLOB rejects the version mismatch.Install py-clob-client-v2. Remove the legacy import. See migration guide.
pip install py-clob-client “works” but bot can’t tradeThe PyPI page still serves V1 (last release 0.34.6, Feb 19, 2026, no deprecation banner). The package installs, signs orders, and the orders just get rejected.Same fix — install py-clob-client-v2.
Custom EIP-712 signers producing rejected sigsYou hard-coded the V1 Exchange verifyingContract (0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E) and version "1".Update to V2 Exchange 0xE111180000d2663C0091e4f400237545B87B996B (Neg Risk: 0xe2222d279d744050d28e00520010520000310F59) and version "2". ClobAuth stays at version "1".

On-chain settlement reverts (the open question)

A separate, public issue worth flagging because it can blindside otherwise-working bots: py-clob-client #338, filed May 2, 2026, reports that ~35% of matched market BUYs on the V2 CTF Exchange are reverting at settlement. The pattern, as documented in the issue:

  • All FAILED settlements consume a uniform ~1.3M gas before reverting with empty reason (0x) — a logic revert, not OOG. Replaying with 10M gas still reverts.
  • Failures cluster on single-maker fills against recently-funded proxy Safes (often <10 minutes old) that held substantial pUSD at the trade block.
  • The failure rate is asymmetric: orders that would have been profitable failed at ~3× the rate of orders that would have lost. P(FAILED | would-be win) ≈ 48% vs. P(FAILED | would-be loss) ≈ 15% over a sample of 34 on-chain trades.

This is not an SDK-side bug — it’s a CTF Exchange V2 contract-level issue. From a bot operator’s perspective:

  • Your CLOB response says MATCHED. The on-chain transaction reverts. Your pUSD balance is unchanged. You did not get the position.
  • Reconcile fills against Trade events on chain, not against the CLOB’s matched response, before acting on them downstream.
  • If you trade taker-side market BUYs, expect a non-trivial slice to fail until Polymarket fixes whatever post-condition is firing in matchOrders. The issue is open with no public root-cause statement.

We’re tracking #338. If a fix or post-mortem ships, we’ll note it here.


Pre-flight checklist (before you call post_order)

A short list that would have saved us hours, derived directly from the issue patterns above:

  • Confirm your import path. from py_clob_client_v2 import ClobClient, Side, OrderArgs, ...not py_clob_client. A stray V1 import alongside V2 silently undoes the migration.
  • Confirm your signature_type matches your wallet type. Check polymarket.com/settings for the funder address. If you don’t have a pre-cutover Safe or Magic account, you’re stuck on the #70 bug — see workaround above.
  • Wrap USDC.e → pUSD. If your wallet is API-only (never visited polymarket.com), your collateral is still USDC.e. Call wrap() on the Collateral Onramp contract — or your get_balance_allowance(AssetType.COLLATERAL) returns zero and every order fails the allowance check. The polymarket.com UI does this for you on first login.
  • Sync update_balance_allowance after approvals. After any on-chain approve() / setApprovalForAll, call client.update_balance_allowance(BalanceAllowanceParams(asset_type=AssetType.COLLATERAL)) to make the CLOB’s cached view catch up.
  • Read the tick size dynamically. Don’t hard-code 0.01 — sports markets are often 0.001.
  • Don’t combine post_only=True with FOK/FAKValueError.
  • Use FAK over FOK for thin books. FOK’s all-or-nothing semantics fail on partial liquidity; FAK at least fills what’s there.
  • Reconcile fills from on-chain Trade events, not from CLOB’s MATCHED response. Settlement reverts are real (#338).
  • Set use_server_time=True if you’re seeing intermittent INVALID_SIGNATURE errors — it costs an extra GET per signed request but eliminates the clock-drift class entirely.

Changelog

DateChange
May 25, 2026Initial publication. Verified against py-clob-client-v2==1.0.1 source, GitHub issues #11, #53, #70 (v2 repo), #65 (TS v2 repo), and #277, #288, #335–#338 (archived V1 repo).

Official Resources

AgentBets Guides

Where This Fits in the Agent Betting Stack

This is a Layer 3 (Trading) maintenance guide — the kind of page that exists so a bot operator at 2am can paste an error message into Google and find the fix. If you’re past the errors and want to build, the Trading Bot Quickstart is the next step.

If your bot is hitting correctness issues rather than SDK errors — wrong fills, missing P&L, settlement reverts — that’s worth treating as an architectural problem (see Agent Betting Security for kill-switch and reconciliation patterns).


This guide is maintained by AgentBets.ai. New error we missed? Let us know on Twitter or open an issue against the relevant Polymarket repo and link it here.

Not financial advice. Built for builders.