Skip to content

Troubleshooting ​

Diagnostic Scripts ​

Two scripts isolate the two external dependencies before you debug the indexer itself.

bash
ETH_URL=http://127.0.0.1:8545 python3 ethtest.py

Prints the current best block and the node's syncing status, or the connection error.

bash
DB_NAME=index python3 pgtest.py

Prints 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:

bash
docker compose exec eth-storage python3 ethtest.py
docker compose exec eth-storage python3 pgtest.py

The Indexer Exits Immediately ​

MessageCause
Set PostgreSQL database in environment variable DB_NAMEDB_NAME is unset
Set Ethereum node URL in environment variable ETH_URLETH_URL is unset
ADDRESS_FILTER_ENABLED must be one of: …The value is not a recognized boolean
Address filter file '…' contains no Ethereum addressesThe list is empty or fully commented out
Invalid Ethereum address in '…' at line NA 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:

bash
psql -v ON_ERROR_STOP=1 -d index -f create_tables.sql
sql
GRANT 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 ​

text
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_BLOCK so 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_syncing and 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.* in psql
  • Confirm the query uses them: EXPLAIN ANALYZE on the generated SQL
  • A query with neither txfrom nor txto has no usable index and will scan the table. This is why the proxy rules reject those requests
  • Prefer: count=exact forces a full scan. Use count=planned or no count at all
  • Large offset values get slower linearly. Paginate by time instead, 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:

  1. Does web_anon exist? SELECT 1 FROM pg_roles WHERE rolname = 'web_anon';
  2. Can it read? SET ROLE web_anon; SELECT * FROM public.max_block;
  3. Does db-anon-role in postgrest.conf name that exact role?
  4. 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.service or docker compose ps
  • Look for Error during synchronization pass in 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_BLOCK of 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:
sql
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_BLOCK or 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.

Released under the GPL-3.0 License.