TL;DR: Your query is fine, it just ran longer than the MCP server allows. Raise `POSTGRES_STATEMENT_TIMEOUT_MS` in the server's `env` block, or narrow the query with a WHERE clause or LIMIT. The timeout exists to stop runaway queries from hanging your agent.

```text
canceling statement due to statement timeout
```

## Fix it

1. Confirm the pattern: queries under ~30 seconds succeed, anything longer dies with this exact message. That is the statement timeout, working as designed.

2. Option A, raise the cap. Add to the server's `env` block in your MCP client config:

```json
{
  "env": {
    "POSTGRES_STATEMENT_TIMEOUT_MS": "120000"
  }
}
```

   That allows 120 seconds. Restart the client.

   Expected: the same query now completes.

3. Option B, make the query cheaper. Add a `WHERE` filter, a `LIMIT`, or an index on the filtered column:

```sql
CREATE INDEX CONCURRENTLY idx_events_created ON events (created_at);
```

   Expected: query time drops under the timeout without changing server config.

## When to use this

- Queries consistently die at the same elapsed time (the configured timeout).
- You are running analytics, backfills, or full-table scans through the MCP server.

## When NOT to use this

- Queries fail immediately with auth, SSL, or connection errors. That is not a timeout.
- A single fast query sometimes hangs forever. That smells like a lock, not the statement timeout.

## Compatibility

- yawlabs/postgres-mcp (POSTGRES_STATEMENT_TIMEOUT_MS, default 30000).
- Other Postgres MCP servers with their own timeout knobs; check the server README.

## Why it happens

Agent-driven database access is dangerous without guardrails: one bad `SELECT *` on a huge table can burn minutes and tokens. So the server sets a Postgres `statement_timeout` per session and cancels anything slower. It is a safety feature, not a bug. Raising it trades safety for convenience, so prefer narrowing the query when you can.

## Edge cases

- `CREATE INDEX` without `CONCURRENTLY` takes a write lock and can block the table. Always use `CONCURRENTLY` on live tables.
- Setting the timeout to 0 disables it entirely. Do that only for a maintenance window, never as a default.
- The timeout applies per statement, not per tool call. A tool that runs several statements can still take longer overall.