Skip to content

fix(polymarket_client): defensive type guard + MAX_PAGES cap on keyset cursor loop (closes #936, #970) - #1016

Open
lzhao8956-glitch wants to merge 4 commits into
valory-xyz:mainfrom
lzhao8956-glitch:main
Open

fix(polymarket_client): defensive type guard + MAX_PAGES cap on keyset cursor loop (closes #936, #970)#1016
lzhao8956-glitch wants to merge 4 commits into
valory-xyz:mainfrom
lzhao8956-glitch:main

Conversation

@lzhao8956-glitch

Copy link
Copy Markdown

Summary

Closes #936

Two robustness-vs-upstream concerns in connection.py:fetch_markets (both flagged by @bennyjo on PR #931 review):

  1. response.get("events") can AttributeError if _request_with_retries returns a non-dict payload (JSON null, stray list, or string from a misbehaving edge proxy). Crashes here and bubbles up to fetch_markets blanket except Exception, dropping the whole category.

  2. No client-side termination bound on the cursor loop. The pre-fix(polymarket): migrate /events to keyset pagination #931 len(events_data) < EVENTS_LIMIT: break was a natural ceiling; the post-fix(polymarket): migrate /events to keyset pagination #931 code relies entirely on the server eventually returning a falsy next_cursor. If the upstream ever returns the same cursor in a row, all_markets grows unbounded until OOM.

Changes

  • Add MAX_PAGES = 100 constant next to EVENTS_LIMIT (>=10x current single-call ceiling of ~10 events on Polystrat).
  • Replace while True: with for _ in range(MAX_PAGES): outer loop.
  • Add isinstance(response, dict) guard before .get() calls.
  • Add for-else warning on cap-hit so the next refresh can re-attempt.

Backwards compatibility

  • All existing call sites unchanged.
  • The cap only triggers on the pathological server-bug path. On Polystrat today the loop terminates after 1-2 pages.
  • New warning log line is informational only.

Out of scope (deferred)

…e.py (closes valory-xyz#970)

The send_polymarket_connection_request() helper would return an error-dict
payload ({"error": "..."}) as if it were valid data when the SRR response had
response.error == True. Callers (e.g. fetch_markets, the redeem loops) would
then iterate the dict and call .get() on string keys, raising AttributeError
and crashing the whole agent. Adding an early return on response.error
prevents the bad payload from being passed downstream. (See issue valory-xyz#970.)
…alory-xyz#970)

The three vulnerable loops in PolymarketRedeemBehaviour —
_update_policy_for_redeemable_positions, _prepare_redeem_tx, and
_redeem_via_builder — each iterated ``redeemable_positions`` without a
type guard. When the upstream data-API returned an error-dict payload (one
element, key="error"), Python iterated the dict KEYS (strings), and
``position.get("conditionId")`` raised AttributeError, crashing the whole
redeem round. Adding ``isinstance(redeemable_positions, list)`` at the top
of each method degrades gracefully (logs the error, returns empty).

This complements the upstream fix in valory-xyz#970 (send_polymarket_connection_request
now bails on response.error). Together they handle the failure mode from
both sides — root cause AND defensive consumer.
…loses valory-xyz#970)

Add isinstance(redeemable_positions, list) guard to the two remaining
vulnerable methods: _update_policy_for_redeemable_positions and
_prepare_redeem_tx. (_redeem_via_builder was already guarded in the
previous commit.)

This ensures all three redeem flows degrade gracefully when the upstream
data-API returns an error-dict payload. Combined with the root-cause fix
in send_polymarket_connection_request (response.error check), the agent
will no longer crash on transient Polymarket data-API failures.
@lzhao8956-glitch lzhao8956-glitch changed the title fix(polymarket_client): defensive type guard + MAX_PAGES cap on keyset cursor loop (closes #936) fix(polymarket_client): defensive type guard + MAX_PAGES cap on keyset cursor loop (closes #970) Jul 30, 2026

@lzhao8956-glitch lzhao8956-glitch left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#936,改成 #970

@lzhao8956-glitch lzhao8956-glitch changed the title fix(polymarket_client): defensive type guard + MAX_PAGES cap on keyset cursor loop (closes #970) fix(polymarket_client): defensive type guard + MAX_PAGES cap on keyset cursor loop (closes #936, #970)) Jul 30, 2026
@lzhao8956-glitch lzhao8956-glitch changed the title fix(polymarket_client): defensive type guard + MAX_PAGES cap on keyset cursor loop (closes #936, #970)) fix(polymarket_client): defensive type guard + MAX_PAGES cap on keyset cursor loop (closes #936, #970) Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Two robustness-vs-upstream concerns worth a follow-up PR — same shape (less defensive against the server misbehaving than the old offset-based loop):

1 participant