Skip to content

ALC-4001: Agent request refused

Alcuin refused a request before it ran. Agents read your databases through Alcuin over MCP, and every request passes a set of checks first. The assistant’s Run read-only uses the same checks, so its messages speak of agents too. The message says which check stopped it and what to do instead.

An agent client receives the refusal as the result of its tool call, with the message, "code": "ENGINE_AGENT_REFUSED" and "refusedBy" naming the check. Most agents then rephrase the request or tell you why they stopped.

In the assistant pane, after Run read-only, the message appears below the proposed SQL.

Each check has a name, which refusedBy reports.

Agents run one SELECT, EXPLAIN or SHOW at a time, and only read. Typical messages:

  • Agents can run one SELECT, EXPLAIN or SHOW at a time; this is a …
  • Send one statement per call; this text holds 3.
  • This statement contains a …, which writes; agents only read.
  • SELECT … INTO creates a table; agents only read.
  • SELECT … FOR UPDATE or FOR SHARE takes row locks; agents only read. Leave the locking clause out.
  • EXPLAIN ANALYZE runs the statement; it is off for agents on production. Explain without ANALYZE, or run it in Alcuin.
  • The statement could not be parsed, so nothing about it can be checked: …

Some functions act outside the read-only transaction, on the server, or on other sessions, and are refused by name. For example: pg_terminate_backend() is refused for agents: it ends another session. Others in the list include pg_cancel_backend, pg_read_file, set_config, pg_notify, advisory locks, large objects and dblink.

A token can be limited to some schemas, and can leave out tables and columns. Typical messages:

  • public.payments is outside this token’s schemas (app, reporting).
  • public.users.email is excluded from this token’s scope.
  • * would read …, which this token’s scope excludes. Name the columns you need.
  • … is a view, which can read columns this token’s scope leaves out. Ask the user to list it under the token’s allowed views if it shows nothing excluded, or to rely on a dedicated role’s grants.
  • app.recent_orders reads app.payments, and app.payments is excluded from this token’s scope. A view, or a materialized view, is checked by what it reads.
  • app.events_2024 holds rows of app.events, which is excluded from this token’s scope. A child table or partition is left out with its parent, and reading a parent checks every child it would read.
  • This statement reads app.child_t.ssn (as part of app.parent_t), which this token’s scope excludes.
  • app.lookup() is a function outside the catalogs: what it reads cannot be checked, so scoped tokens cannot call it. A dedicated role’s grants are the control for functions. This covers functions called directly, by a view, or behind an operator.
  • TOAST tables, column statistics (pg_stats, pg_statistic_ext_data) and pg_stat_activity are refused for scoped tokens; the activity tool shows other sessions, filtered by the scope.

The scope is checked statement by statement, right before each runs. A dedicated role’s grants are the lasting boundary.

This session has read its 5,000 rows. Start a new session, or narrow the question with WHERE and aggregates. When several calls run at once, it reads The calls running now have taken the rest of this session’s 5,000 rows….

The token does not cover the connection the agent named, or covers it no longer:

  • There is no connection named … for this token. list_connections names the ones there are.
  • Several connections are named …; ask the user to rename one.
  • This token does not include this connection.
  • This token does not cover production connections.
  • This connection moved from staging to production since the token was given it. Confirm it again in Alcuin’s Settings, under Agents.
  • This connection now points at a different server than when the token was given it. Confirm it again in Alcuin’s Settings, under Agents.

Your Safety rules would ask before running this statement (ALC-3004). Agents are never asked to confirm, so the message ends Agents are never asked to confirm: run it in Alcuin.

  • statement or function: the agent can usually rephrase within the limits. If the work needs a write or a refused function, run it yourself in an Alcuin query tab.
  • scope: ask the agent to name only the columns it needs. To change what a token may read, open Settings, under Agents, make a new token with the scope you want, and revoke the old one with Revoke token. Connect an agent walks through the token’s scope.
  • budget: start a new agent session, or ask a narrower question with WHERE, GROUP BY and aggregates.
  • grant: for a moved or repointed connection, open Settings, under Agents. The token’s entry shows Suspended: with the reason; choose Allow again if the change was intended. A token without production access can’t be allowed again on a connection that is now production. To add a connection or production access, make a new token. For two connections with the same name, rename one.
  • confirm: run the statement yourself in Alcuin, where you can see it and confirm it.

The engine reports this as ENGINE_AGENT_REFUSED. Quote either name in an issue.