# Custom Exception Types in grep-mcp: GrepAPIError, Timeout, and Rate Limit Handling

> Explore custom exception types in grep-mcp: GrepAPIError, Timeout, and Rate Limit. Learn how these exceptions handle specific API failure modes, improving your error handling.

- Repository: [gal peretz/grep-mcp](https://github.com/galprz/grep-mcp)
- Tags: internals
- Published: 2026-02-16

---

**`grep-mcp` defines three custom exception types—`GrepAPIError`, `GrepAPITimeoutError`, and `GrepAPIRateLimitError`—to handle specific failure modes when querying the grep.app API, including generic HTTP errors, 30-second timeouts, and rate-limit breaches.**

The `grep-mcp` project provides a Model Context Protocol (MCP) server that interfaces with the grep.app code search API. When interacting with external APIs, robust error handling is essential. This repository implements a hierarchical exception structure in **[`src/grep_mcp/server.py`](https://github.com/galprz/grep-mcp/blob/main/src/grep_mcp/server.py)** that distinguishes between transient network issues, quota violations, and general API failures.

## The Three Custom Exception Types in grep-mcp

All custom exception types reside in **[`src/grep_mcp/server.py`](https://github.com/galprz/grep-mcp/blob/main/src/grep_mcp/server.py)** and inherit from Python's built-in `Exception` class, forming a hierarchy that allows for granular error handling.

### GrepAPIError: The Base Exception Class

**`GrepAPIError`** serves as the foundational exception class for all API-related problems in the codebase. Defined at **line 21** in [`src/grep_mcp/server.py`](https://github.com/galprz/grep-mcp/blob/main/src/grep_mcp/server.py), this generic exception captures any non-successful HTTP response that does not fall into the specific categories of 404 (no results) or 429 (rate limit).

The exception is raised at **line 44** when the code detects `response.status != 200`, making it the catch-all for unexpected API behavior.

### GrepAPITimeoutError: Handling Request Timeouts

**`GrepAPITimeoutError`** is a specialized subclass of `GrepAPIError` defined at **line 26**. This exception handles cases where the underlying `aiohttp` client request exceeds the configured 30-second timeout limit.

The triggering mechanism occurs at **line 52**, where the code catches `asyncio.TimeoutError` and re-raises it as `GrepAPITimeoutError`. This abstraction allows calling code to distinguish between network timeouts and other API failures without inspecting low-level asyncio exceptions.

### GrepAPIRateLimitError: Managing Rate Limit Breaches

**`GrepAPIRateLimitError`** handles HTTP 429 responses from the grep.app API. Defined at **line 31** as another subclass of `GrepAPIError`, this exception specifically signals that the client has exceeded the API's rate limiting quotas.

The check occurs at **line 30**, where the code inspects for `response.status == 429`, and immediately raises `GrepAPIRateLimitError` at **line 31**. This distinct exception type enables implementing backoff strategies or quota management in client code.

## When and How These Exceptions Are Triggered

The exception handling logic is centralized in the `grep_query` function within [`src/grep_mcp/server.py`](https://github.com/galprz/grep-mcp/blob/main/src/grep_mcp/server.py). The triggering conditions follow a specific precedence:

1. **Rate Limit Check**: First, the code checks for HTTP 429 at line 30, raising `GrepAPIRateLimitError` immediately.
2. **Generic API Error**: If the status is not 200, line 44 raises `GrepAPIError` for any other non-success status.
3. **Timeout Detection**: If the `aiohttp` request raises `asyncio.TimeoutError`, line 52 catches it and re-raises as `GrepAPITimeoutError`.

These exceptions are then caught within `grep_query` (lines 56-64) to convert them into user-friendly error messages for the MCP protocol.

## Handling Custom Exceptions in Your Code

When integrating `grep-mcp` into your applications, you should catch these specific exceptions to implement appropriate retry logic or user notifications.

```python
from grep_mcp.server import (
    grep_query,
    GrepAPIError,
    GrepAPITimeoutError,
    GrepAPIRateLimitError
)

async def run_search():
    try:
        result = await grep_query("asyncio.run", language="python")
        print(result)
    except GrepAPIRateLimitError as e:
        # Implement exponential backoff

        print(f"Rate limit exceeded: {e}")
        await asyncio.sleep(60)
    except GrepAPITimeoutError as e:
        # Handle network timeouts specifically

        print(f"Request timed out after 30 seconds: {e}")
    except GrepAPIError as e:
        # Catch-all for other API issues

        print(f"API error occurred: {e}")

```

For unit testing, you can manually raise these exceptions to verify your error handling paths:

```python
from grep_mcp.server import GrepAPITimeoutError

def test_timeout_handling():
    with pytest.raises(GrepAPITimeoutError):
        raise GrepAPITimeoutError("Simulated timeout for testing")

```

## Summary

- **`GrepAPIError`** is the base exception class defined at line 21 in [`src/grep_mcp/server.py`](https://github.com/galprz/grep-mcp/blob/main/src/grep_mcp/server.py), raised for generic HTTP errors (non-200 status codes) at line 44.
- **`GrepAPITimeoutError`** subclasses the base error at line 26, triggered when `aiohttp` requests exceed the 30-second timeout and are caught at line 52.
- **`GrepAPIRateLimitError`** subclasses the base error at line 31, specifically handling HTTP 429 responses detected at line 30.
- All three exceptions are centralized in [`src/grep_mcp/server.py`](https://github.com/galprz/grep-mcp/blob/main/src/grep_mcp/server.py) and enable granular error handling for the grep.app API integration.

## Frequently Asked Questions

### What is the base exception class in grep-mcp?

The base exception class is **`GrepAPIError`**, defined at line 21 in [`src/grep_mcp/server.py`](https://github.com/galprz/grep-mcp/blob/main/src/grep_mcp/server.py). It serves as the parent class for all API-related exceptions in the project, including `GrepAPITimeoutError` and `GrepAPIRateLimitError`. You should catch this exception when you want to handle any API failure generically without distinguishing between specific error types.

### How do I catch timeout errors when using grep-mcp?

Import **`GrepAPITimeoutError`** from `grep_mcp.server` and include it in your exception handling hierarchy. This exception is raised when the underlying `aiohttp` client exceeds the 30-second timeout configured in the codebase. The exception is caught at line 52 in [`src/grep_mcp/server.py`](https://github.com/galprz/grep-mcp/blob/main/src/grep_mcp/server.py) and re-raised as `GrepAPITimeoutError`, allowing your application to implement retry logic or notify users of network latency issues.

### What HTTP status code triggers GrepAPIRateLimitError?

**HTTP 429** triggers `GrepAPIRateLimitError`. The code explicitly checks for `response.status == 429` at line 30 in [`src/grep_mcp/server.py`](https://github.com/galprz/grep-mcp/blob/main/src/grep_mcp/server.py) and raises this specific exception at line 31. This status code indicates that the client has exceeded the rate limits imposed by the grep.app API, and catching this exception allows you to implement backoff strategies or queue requests until the quota resets.

### Where are the custom exception types defined in the source code?

All three custom exception types—`GrepAPIError`, `GrepAPITimeoutError`, and `GrepAPIRateLimitError`—are defined in **[`src/grep_mcp/server.py`](https://github.com/galprz/grep-mcp/blob/main/src/grep_mcp/server.py)**. The base class `GrepAPIError` appears at line 21, `GrepAPITimeoutError` at line 26, and `GrepAPIRateLimitError` at line 31. This centralized location makes it easy to import and handle these exceptions when building applications that integrate with the grep-mcp server.