Troubleshooting
When a statement does not run
Run tablebelt doctor first. Each check prints ok, warn, or fail, with a fix. It covers config, secrets present, upstream reachable, TLS on both sides, snapshot and restore privileges, transaction pooler detection, schema version, clock skew, and cloud reachability when the proxy is linked.
tablebelt doctor
Doctor warnings you will actually hit
- Transaction pooler: switch to the direct or session port. See Hosts.
- Admin role cannot read app tables, or cannot own them for restore: the restore will fail later. Fix the grants in the role SQL.
- Cloud unreachable: holds stay pending. Local approve still works if
cloud.local_approvalsis true.tablebelt statusshows the last error, including a revoked key. - Clock skew: email links and webhook timestamps use unix time. Fix NTP on the proxy host.
Error codes
Drivers surface TB codes as a database error. The message tells the agent what happened and what to ask you. It also tells the agent not to rewrite the SQL to dodge the hold.
| Code | When | What to do |
|---|---|---|
| notice | Hold created | Names the hold id and how long it will wait. Pro and Team add a dashboard link. |
| TB001 | Block timeout, still pending | Not executed. Ask a person to approve, then run the same statement again. |
| TB002 | Denied | Names who denied it and their note. Do not rewrite the statement to get around it. |
| TB003 | Unparsed, too large, or classifier error | The statement did not run. |
| TB004 | Snapshot failed after approval | The statement did not run. Retry, or approve with an explicit no-snapshot acknowledgement. |
| TB005 | Protected object | Schema tablebelt is not reachable through the proxy. |
| TB006 | Too many pending holds | Default cap is 100. This statement did not run. |
| TB007 | Hold expired | Run it again if you still want a new hold. |
| TB008 | Policy deny | A rule, COPY PROGRAM, FunctionCall, or PREPARE of a held statement. |
| TB009 | Replication or disallowed startup options | The connection is refused. |
| TB010 | Hold store unavailable | Statements that would be held do not run. Allowed statements still pass. |
| 57014 | Client cancelled while held | The hold stays pending. |
| 25P02 | Next statement after a rejected hold inside a transaction | The proxy matches Postgres: the transaction looks aborted until ROLLBACK. |
A hold that seems stuck
tablebelt heldshows pending rows.tablebelt show hold_0007shows the statement, tables, and snapshot plan.- Approve with
tablebelt approve hold_0007, or in the dashboard on Pro and Team. - If the plan has no snapshot, pass
--no-snapshot. A fresh install deniesDROP DATABASEbefore you get that far. - After approval, run the same statement again if the block timeout (TB001) already returned. One approval runs it once.
- Inside an open transaction, a rejected hold makes later statements look aborted (25P02) until ROLLBACK. That matches Postgres.
Exit codes for the CLI: 0 ok, 1 error, 2 usage, 3 not found, 4 refused (policy or a declined confirmation), 5 proxy not reachable.