Troubleshooting
Diagnostic Scripts
Two scripts isolate the two external dependencies before you debug the indexer itself.
ETH_URL=http://127.0.0.1:8545 python3 ethtest.pyPrints the current best block and the node's syncing status, or the connection error.
DB_NAME=index python3 pgtest.pyPrints MAX(block) from ethtxs, or the connection error with the password redacted.
Under Compose, run them inside the container so the network and environment match:
docker compose exec eth-storage python3 ethtest.py
docker compose exec eth-storage python3 pgtest.pyThe Indexer Exits Immediately
| Message | Cause |
|---|---|
Set PostgreSQL database in environment variable DB_NAME | DB_NAME is unset |
Set Ethereum node URL in environment variable ETH_URL | ETH_URL is unset |
ADDRESS_FILTER_ENABLED must be one of: … | The value is not a recognized boolean |
Address filter file '…' contains no Ethereum addresses | The list is empty or fully commented out |
Invalid Ethereum address in '…' at line N | A line is neither a comment nor a valid address |
Unable to read address filter file '…' | Missing file, wrong path, or wrong permissions |
Unable to connect to PostgreSQL database: … | Wrong host, database, credentials, or the server is down |
All of these exit with a non-zero status, which is what systemd Restart=on-failure and Docker restart policies react to.
Unable to initialize the database checkpoint
The schema is missing or the role lacks permissions on sync_state. Apply the schema and the grants:
psql -v ON_ERROR_STOP=1 -d index -f create_tables.sqlGRANT SELECT, INSERT, UPDATE ON public.sync_state TO api_user;Under Compose this happens when the database volume predates sync_state — the entrypoint script runs only on an empty data directory. Apply the schema manually, as shown in Upgrading.
Waiting for the Node to Synchronize
Waiting for Ethereum node to finish synchronizing… (retrying in 5 minutes)The indexer refuses to index from a node that reports eth_syncing, because a partially synced node returns incomplete data. Wait for the client to finish. This can take hours or days for a fresh node and is not an indexer problem.
Indexing Is Too Slow
The dominant cost is one eth_getTransactionReceipt call per stored transaction, on top of one block fetch per block.
- Use a local node. A remote or metered RPC endpoint makes backfill impractical
- Prefer IPC, then WebSocket, then HTTP
- Raise
START_BLOCKso there is less history to walk - Enable the address filter, which skips receipt lookups for transactions that do not match
- Create indexes after the initial backfill, not before
- Check that the node itself is not the bottleneck, with
eth_syncingand the client's own metrics
PERIOD does not throttle the backfill. Within a pass, blocks are processed continuously.
Queries Are Slow or Time Out
- Confirm the indexes exist:
\di public.*inpsql - Confirm the query uses them:
EXPLAIN ANALYZEon the generated SQL - A query with neither
txfromnortxtohas no usable index and will scan the table. This is why the proxy rules reject those requests Prefer: count=exactforces a full scan. Usecount=plannedor no count at all- Large
offsetvalues get slower linearly. Paginate bytimeinstead, as shown in the API reference - Run
ANALYZE public.ethtxs;after a large backfill so the planner has current statistics
API Returns 500 for Every Request
Almost always the anonymous role. Check, in this order:
- Does
web_anonexist?SELECT 1 FROM pg_roles WHERE rolname = 'web_anon'; - Can it read?
SET ROLE web_anon; SELECT * FROM public.max_block; - Does
db-anon-roleinpostgrest.confname that exact role? - Can the PostgREST login role assume it?
GRANT web_anon TO api_user;
An unbounded GET /ethtxs on a large table also returns 500 when db-max-rows is unset, because the JSON response exceeds PostgreSQL's 1 GB value limit.
/max_block Returns null
max is null when no block has been processed and no transaction rows exist. On a new deployment, wait one PERIOD. If it stays null:
- Check the indexer logs for errors
- Confirm the node's height is above
START_BLOCK - Confirm the indexer and PostgREST point at the same database
A permission error rather than null means GRANT SELECT ON public.max_block TO web_anon; is missing.
/max_block Stops Advancing
- Is the process running?
systemctl status ethsync.serviceordocker compose ps - Look for
Error during synchronization passin the logs; the pass is abandoned without advancing the checkpoint, and the next pass retries - With the filter enabled, a broken address file stops processing by design. Fix the file
- Check free disk. A full disk stops PostgreSQL writes, and the indexer will log the failure every pass
Missing Transactions
Before treating it as a bug, confirm the transfer is in scope. The indexer stores native ETH transfers and direct transfer(address,uint256) calls only. Internal transfers, transferFrom, multisig, router, batch, and aggregator flows, and non-ERC-20 token standards are never indexed. See what gets indexed.
Then check:
- Is the block above
START_BLOCK? - Is the address filter on, and does the address appear in the list?
- Is the block within
CONFIRMATIONS_BLOCKof the head, and therefore not yet indexed? - Was the address added to the filter after the block was processed? There is no automatic backfill
Duplicate Rows After a Crash
The table has no uniqueness constraint on txhash, so a crash between the insert and the commit could in principle duplicate a block's rows. The startup routine prevents this by deleting the highest indexed block and rewinding the checkpoint by one before the first pass. If duplicates do appear, re-index the affected range as described in Upgrading.
Disk Filling Up
- Check the split between table and indexes:
SELECT pg_size_pretty(pg_total_relation_size('public.ethtxs')) AS total,
pg_size_pretty(pg_relation_size('public.ethtxs')) AS table_only;- Drop the legacy indexes if you still have all eight, which frees roughly 90–110 GB on a one-year dataset. See Upgrading
- Reduce scope with a higher
START_BLOCKor the address filter, then re-index - Review PostgreSQL autovacuum and WAL retention settings, which are outside this project but frequently the actual cause
Getting Help
Open an issue at github.com/Adamant-im/ETH-transactions-storage/issues with the indexer version from /max_block, the deployment mode, the execution client and version, the PostgreSQL version, and the relevant log lines. Remove credentials before pasting configuration.