Skip to content

models: OrderBook.timestamp uses int() instead of EpochMsTimestamp, bypassing the whitespace-rejection guard #63

@Nexory

Description

@Nexory

Current code

src/polymarket/models/clob/order_book.py, lines 33–52:

@field_validator("timestamp", mode="before")
@classmethod
def _parse_timestamp(cls, value: object) -> datetime | None:
    if value in (None, ""):
        return None
    if isinstance(value, datetime):
        return value
    if isinstance(value, str):
        try:
            ms = int(value)   # ← accepts " 1710000000000", "-1", "+1"; no isdecimal() guard
        except ValueError as error:
            msg = f"invalid epoch-ms timestamp: {value!r}"
            raise ValueError(msg) from error

The canonical EpochMsTimestamp type (src/polymarket/models/clob/_validators.py, line 42) rejects whitespace-padded and sign-prefixed strings via value.isdecimal(). Its own docstring (line 41) explicitly calls out the gap:

"Unsigned digits only; int() alone would accept '-1', '+1', ' 1 ', etc."

The test suite (test_streams_market_events.py:253–256) asserts that " 1710000000000" and "1710000000000 " raise ValidationError for EpochMsTimestamp. OrderBook._parse_timestamp silently accepts both.

Reproduction

from polymarket.models.clob.order_book import OrderBook

# Should raise ValidationError — does not
ob = OrderBook(timestamp=" 1710000000000", bids=[], asks=[])
print(ob.timestamp)  # prints a valid datetime; no error raised

# EpochMsTimestamp (used by every sibling model) correctly rejects this
from polymarket.models.clob._validators import EpochMsTimestamp
from pydantic import TypeAdapter
ta = TypeAdapter(EpochMsTimestamp)
ta.validate_python(" 1710000000000")  # raises ValidationError

Impact

OrderBook is the return type of ClobClient.get_order_book() and the payload type for the book WebSocket event. It is the most-used streaming model. A wire message with a whitespace-padded timestamp (malformed feed, protocol edge case, or future spec change) will parse silently into OrderBook but raise ValidationError on any sibling model that processes the same timestamp field, creating an inconsistent validation surface. Callers that rely on uniform rejection behaviour across all CLOB models will see silent corruption only for OrderBook.

Historical context

order_book.py was created in commit 26865e6 (May 14) before EpochMsTimestamp existed. EpochMsTimestamp was introduced in commit 599b8e3 (May 15) and was applied to all eight timestamp fields in market_events.py (MarketBookPayload, MarketPriceChangePayload, MarketLastTradePricePayload, MarketTickSizeChangePayload, MarketBestBidAskPayload, NewMarketPayload, MarketResolvedPayload), but order_book.py was never updated.

Suggested fix

Replace the bespoke field_validator with the canonical type:

# before
timestamp: datetime | None = None

@field_validator("timestamp", mode="before")
@classmethod
def _parse_timestamp(cls, value: object) -> datetime | None:
    ...  # 20-line bespoke validator

# after
from polymarket.models.clob._validators import EpochMsTimestamp

timestamp: EpochMsTimestamp = None

This removes the duplication, enforces the isdecimal() guard, and aligns OrderBook with every other CLOB model that carries a timestamp field.

Related

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions