fetch_builder_fee_rates / fetch_builder_fee_rates_sync in src/polymarket/_internal/actions/orders/market_data.py:75-92 issue GET /fees/builder-fees/<code> and don't translate or guard the 404 case. Callers in src/polymarket/_internal/actions/orders/market.py:185-217 invoke them unconditionally whenever params.builder_code is set and non-zero, so a market-buy with an unregistered builder code crashes during fee resolution and never reaches order construction.
Current code
# src/polymarket/_internal/actions/orders/market_data.py:75-92
async def fetch_builder_fee_rates(ctx: AsyncClientContext, *, builder_code: str) -> BuilderFeeRates:
validated = validate_builder_code(builder_code)
if validated == BYTES32_ZERO:
raise UserInputError(
"builder_code must be a real builder; zero (0x000…000) represents no attribution."
)
data = await ctx.clob.get_json(f"/fees/builder-fees/{validated}")
return BuilderFeeRates.parse_response(data)
def fetch_builder_fee_rates_sync(ctx: SyncClientContext, *, builder_code: str) -> BuilderFeeRates:
validated = validate_builder_code(builder_code)
if validated == BYTES32_ZERO:
raise UserInputError(...)
data = ctx.clob.get_json(f"/fees/builder-fees/{validated}")
return BuilderFeeRates.parse_response(data)
# src/polymarket/_internal/actions/orders/market.py:185-191
fee_info = await fetch_platform_fee_info(ctx, condition_id=condition_id)
builder_taker_fee_rate = Decimal(0)
if params.builder_code is not None and params.builder_code != BYTES32_ZERO:
rates = await fetch_builder_fee_rates(ctx, builder_code=params.builder_code)
builder_taker_fee_rate = rates.taker
return adjust_buy_amount_for_fees(...)
ctx.clob.get_json raises RequestRejectedError(status=404) on a missing builder code; the caller does not catch it and the entire market-order construction aborts.
Live server behavior
$ curl -s -w '\n%{http_code}\n' \
'https://clob.polymarket.com/fees/builder-fees/0xabcdef…cdef'
{"error":"builder code not found"}
404
The 404 is well-defined ("builder code not found"). CORS is * so this is a server-state issue, not a transport restriction.
Same shape on clob-client-v2
Structurally the same as Polymarket/clob-client-v2#51 (ts/v2-transitional). Polymarket/clob-client-v2#72 by @Cassxbt proposes a silent try/catch fix — open since 2026-05-21 without maintainer engagement, so the design direction looks unresolved.
Design question
Three approaches:
- Silent skip: catch
RequestRejectedError where status == 404 in fetch_builder_fee_rates, return BuilderFeeRates(maker=Decimal(0), taker=Decimal(0)). Order proceeds without builder attribution — silently. Bad UX for the case the caller actually wanted attribution.
- Typed error mapping: introduce
UnknownBuilderCodeError(RequestRejectedError) (mirrors the existing pattern in polymarket/_internal/eoa/rpc.py:13: class JsonRpcCallError(RequestRejectedError):). fetch_builder_fee_rates re-raises 404 as UnknownBuilderCodeError; market-order callers can catch it and decide policy.
- Server-side: distinguish unknown-vs-disabled / not-yet-propagated.
I'd lean (2) — preserves information for the caller and matches the existing typed-error pattern. But this is a design call and could go differently depending on what the platform contract for "unknown builder code" is meant to be.
Impact
Limit orders are unaffected (they don't fetch fees in place.py). The break is scoped to market orders with builder attribution — prepare_market_order_draft and the _resolve_market_buy_amount / _resolve_market_buy_amount_sync paths via market.py:185-217. A builder who pastes a code that isn't yet registered (testing, propagation lag, copy/paste of wrong env) gets a RequestRejectedError from a prepare_market_* call rather than from a fee lookup, which is hard to debug.
Cross-SDK
Same pattern in ts-sdk: Polymarket/ts-sdk#74 (packages/client/src/actions/clob.ts:398-409 + packages/client/src/actions/orders/market.ts:222-232).
Happy to PR once you've picked a direction.
fetch_builder_fee_rates/fetch_builder_fee_rates_syncinsrc/polymarket/_internal/actions/orders/market_data.py:75-92issueGET /fees/builder-fees/<code>and don't translate or guard the 404 case. Callers insrc/polymarket/_internal/actions/orders/market.py:185-217invoke them unconditionally wheneverparams.builder_codeis set and non-zero, so a market-buy with an unregistered builder code crashes during fee resolution and never reaches order construction.Current code
ctx.clob.get_jsonraisesRequestRejectedError(status=404)on a missing builder code; the caller does not catch it and the entire market-order construction aborts.Live server behavior
The 404 is well-defined ("builder code not found"). CORS is
*so this is a server-state issue, not a transport restriction.Same shape on clob-client-v2
Structurally the same as Polymarket/clob-client-v2#51 (ts/v2-transitional). Polymarket/clob-client-v2#72 by @Cassxbt proposes a silent try/catch fix — open since 2026-05-21 without maintainer engagement, so the design direction looks unresolved.
Design question
Three approaches:
RequestRejectedErrorwherestatus == 404infetch_builder_fee_rates, returnBuilderFeeRates(maker=Decimal(0), taker=Decimal(0)). Order proceeds without builder attribution — silently. Bad UX for the case the caller actually wanted attribution.UnknownBuilderCodeError(RequestRejectedError)(mirrors the existing pattern inpolymarket/_internal/eoa/rpc.py:13: class JsonRpcCallError(RequestRejectedError):).fetch_builder_fee_ratesre-raises 404 asUnknownBuilderCodeError; market-order callers can catch it and decide policy.I'd lean (2) — preserves information for the caller and matches the existing typed-error pattern. But this is a design call and could go differently depending on what the platform contract for "unknown builder code" is meant to be.
Impact
Limit orders are unaffected (they don't fetch fees in
place.py). The break is scoped to market orders with builder attribution —prepare_market_order_draftand the_resolve_market_buy_amount/_resolve_market_buy_amount_syncpaths viamarket.py:185-217. A builder who pastes a code that isn't yet registered (testing, propagation lag, copy/paste of wrong env) gets aRequestRejectedErrorfrom aprepare_market_*call rather than from a fee lookup, which is hard to debug.Cross-SDK
Same pattern in ts-sdk: Polymarket/ts-sdk#74 (
packages/client/src/actions/clob.ts:398-409+packages/client/src/actions/orders/market.ts:222-232).Happy to PR once you've picked a direction.