Skip to content

Polymarket redeem: data-API failure crashes the whole agent (unhandled error response) #970

Description

@LOCKhart07

Summary

A transient Polymarket data-API failure during a redeem round crashes the entire agent with an unhandled AttributeError, instead of being retried or no-op'd. This is a long-standing latent bug (predates all recent Polymarket redeem work), only triggered when the data-api.polymarket.com/positions call fails.

Observed crash

From a Pearl agent_runner.log (Polystrat, agent sc-c427c8fc...):

[ERROR] Error fetching positions: HTTPSConnectionPool(host='data-api.polymarket.com', port=443):
        Max retries exceeded with url: /positions?...&redeemable=True
        (Caused by ConnectTimeoutError(... connect timeout=10))
[INFO]  Received Srr message: ... error=True, payload={"error": "Error fetching positions: ... timed out ..."}
[INFO]  Fetched 1 redeemable positions
...
File ".../decision_maker_abci/behaviours/polymarket_reedem.py", line 77, in _update_policy_for_redeemable_positions
    condition_id = position.get("conditionId")
AttributeError: 'str' object has no attribute 'get'

Error: AEA was terminated cause exception `... TraderConsensusBehaviour ...`

The agent process is terminated by the framework (uncaught AEAActException).

Root cause

Two layered defects:

  1. send_polymarket_connection_request (packages/valory/skills/market_manager_abci/behaviours/base.py:100) returns json.loads(response.payload) and never checks response.error. When the connection fails it returns the dict {"error": "...timed out..."} instead of a list of positions. This affects every caller, not just redeem.

  2. PolymarketRedeemBehaviour (packages/valory/skills/decision_maker_abci/behaviours/polymarket_reedem.py) never validates the response shape:

    • len({"error": ...}) is 1, so it logs the misleading Fetched 1 redeemable positions.
    • _update_policy_for_redeemable_positions (line 76–77) does for position in redeemable_positions: — iterating a dict yields its keys, so position == "error" (a str), and position.get("conditionId") raises AttributeError.
    • The same crash exists in the _prepare_redeem_tx and _redeem_via_builder loops (they also call position.get(...)).

A recoverable network blip becomes a fatal agent crash.

Was this always a bug?

Yes — not a regression:

  • The unchecked response.error has existed since 924f66b3 refactor: polymarket fetch rounds (#744).
  • d325c831 (e-greedy accuracy store at redemption) only moved the crash site earlier (line 197 → _update_policy_for_redeemable_positions); before it, the same {"error": ...} dict crashed a few lines later in the redeem loops.
  • 35bb90d1 fix(polymarket): guard malformed positions in redeem loops does not fix this: its if condition_id is None: continue guards run after position.get(...), so they defend against a dict with missing keys, not against position being a str — and it never touched _update_policy_for_redeemable_positions.

It stayed invisible because the happy path always returns a real list; only an API failure during a redeem round exercises it.

Suggested fix

  1. In send_polymarket_connection_request: check response.error (and/or validate the deserialized payload type) and surface failures distinctly so callers can retry/no-op rather than receiving an error dict typed as data.
  2. In PolymarketRedeemBehaviour: if the fetched result is not a list (e.g. an {"error": ...} dict), log and treat the round as "no redeemable positions / retry" instead of iterating it. Add a type guard at the top of the three position loops.
  3. Regression test: connection returns {"error": ...} → redeem round degrades gracefully, agent does not crash.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions