Skip to content

Agent tool retries are broken: transient API failures return strings instead of raising ModelRetry #5

Description

@Varadraj75

What's broken

The agrinet_agent is configured with retries=3 in agents/agrinet.py, but the retry logic never fires for the failures that actually happen in production.

All four BAP-calling tools (weather, mandi, scheme, warehouse) import ModelRetry from pydantic_ai but only raise it for generic Exception. The specific transient failures (timeouts, non-200 status codes, connection errors) return plain strings instead. Since pydantic_ai treats a returned string as a successful tool result, the agent passes the error message straight to the LLM, which then just tells the farmer "service unavailable."

The returned string in some cases literally says "Retrying" but the code never actually retries.

Separately, agents/tools/search.py has no exception handling at all around the Marqo client call. A network blip or missing index raises an unhandled exception that crashes the entire agent request with a 500.

Where exactly

The pattern is identical across four files:

agents/tools/weather.py (lines 432-451)
agents/tools/mandi.py (lines 256-277)
agents/tools/scheme.py (lines 246-264)
agents/tools/warehouse.py (lines 318-336)

# What the code does now (same in all four files):

if response.status_code != 200:
    return "Weather service unavailable. Retrying"   # string, no retry happens

except requests.Timeout:
    return "Weather request timed out."               # string, no retry happens

except requests.RequestException as e:
    return f"Weather request failed: {str(e)}"        # string, no retry happens

except Exception as e:
    raise ModelRetry(...)                              # only this one actually retries

And in agents/tools/search.py (line 91):

results = client.index(index_name).search(**search_params)['hits']
# no try/except, no ModelRetry import, nothing

Why this matters

The BAP endpoints (IMD weather, APMC mandi prices, govt scheme portal, warehouse lookup) are external government APIs. Transient 502s and timeouts are common. Right now, every time one of those APIs has a momentary hiccup, the farmer gets a dead-end "service unavailable" response even though the agent is configured to retry 3 times.

For the Marqo search tool, any network issue between the app container and Marqo crashes the full request with an unhandled exception instead of falling back to the LLM's general knowledge.

Proposed fix

For weather.py, mandi.py, scheme.py, warehouse.py:

Replace the string returns on transient failures with raise ModelRetry(...) so pydantic_ai's built-in retry logic actually kicks in:

if response.status_code != 200:
    logger.error(f"Weather API returned status code {response.status_code}")
    raise ModelRetry(f"Weather API returned {response.status_code}, retrying")

except requests.Timeout:
    logger.error("Weather API request timed out")
    raise ModelRetry("Weather API request timed out, retrying")

except requests.RequestException as e:
    logger.error(f"Weather API request failed: {e}")
    raise ModelRetry(f"Weather API request failed: {str(e)}")

Keep the UnexpectedModelBehavior catch as-is since that already handles the case where retries are exhausted.

For search.py:

Add ModelRetry to imports and wrap the Marqo call:

try:
    results = client.index(index_name).search(**search_params)['hits']
except Exception as e:
    logger.error(f"Marqo search failed: {e}")
    raise ModelRetry(f"Search service error: {str(e)}")

Files changed

  • agents/tools/weather.py
  • agents/tools/mandi.py
  • agents/tools/scheme.py
  • agents/tools/warehouse.py
  • agents/tools/search.py

Would like to work on this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    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