Node operations issues

Diagnostic flows for the operational issues node operators hit most often — slow or stopped sync, faster startup, conditional shutdown at a block height, JVM and TCP-traffic tuning, the different resultCode sync error, and the --debug 80 ms bypass.

📘

Prerequisites

This page covers operational issues you encounter when running a TRON full node — slow or stuck block sync, JVM and OS-level tuning, conditional shutdown, the sync-stopping different resultCode error, and the debug-mode bypass for the 80 ms execution time limit.

For deployment basics (hardware, JDK choice, starting and stopping a node), see Deploy a node. For database utilities (Lite Fullnode pruning, engine conversion, LevelDB startup pre-warm), see Node maintenance toolkit.


Slow or stopped block sync

A running node falls behind Mainnet or stops applying new blocks. Three things to check in order:

1. Hardware

Recommended: 16-core CPU, 32 GB RAM, 3 TB or more of SSD storage. Below that, the node may stop syncing when the chain hits a complex contract call. Verify your physical core count:

$ cat /proc/cpuinfo | grep -e "cpu cores" -e "siblings" | sort | uniq
cpu cores : 8
siblings  : 16

Both cpu cores and siblings should be ≥ 16 on production. See Deploy a node — hardware requirements for the full table.

2. Verification time tolerance

If hardware is borderline, raise the transaction-verification time tolerance in config.conf:

vm = {
  supportConstant = false
  minTimeRatio = 0.0
  maxTimeRatio = 20.0            # raise to 50 on very slow hardware
  saveInternalTx = false
}

maxTimeRatio multiplies the 80 ms execution budget at the verification stage — a higher value lets slow nodes accept blocks containing heavy contract calls that would otherwise time out locally.

3. JVM heap and GC

First match the JDK version to the CPU architecture: use only JDK 8 on x86_64 / amd64, and only JDK 17 on ARM64 / aarch64. Then configure the JVM heap and GC options for that environment:

# x86_64 / amd64: JDK 8 only, with the project-recommended CMS GC
java -Xms9G -Xmx9G -XX:+UseConcMarkSweepGC -jar FullNode.jar -c config.conf

# ARM64 / aarch64: JDK 17 only, with the project-recommended ZGC
java -Xmx9G -XX:+UseZGC -jar FullNode.jar -c config.conf
  • These heap values target a 16 GB host. On hosts with at least 32 GB of RAM, set -Xmx to about 40% of physical RAM, then tune from node load and monitoring data.
  • GC option must come before -jar.

See Deploy a node — recommended JVM parameters for the complete configuration.


Speed up node startup

For nodes using LevelDB, the LevelDB Startup Optimization Tool (shipped with the Toolkit JAR) pre-warms the manifest file and LevelDB metadata to reduce startup time and initial memory usage.

See the Node maintenance toolkit for the full toolkit utility list, and the Toolkit User Guide — LevelDB Startup Optimization Tool for the exact CLI invocation.

This optimization is most useful when:

  • You restart the node frequently (development / staging deployments)
  • Startup time is dominated by LevelDB compaction or recovery passes

Stop the node at a specific block height

For data backup or query-only operations, configure automatic shutdown in config.conf:

Starting with GreatVoyage-v4.8.2, this configuration also applies to SolidityNode.

node {
  shutdown {
    # Set exactly ONE of the three options below.
    # Stop at a specific block height
    BlockHeight = 33350800

    # Stop at a specific time (Quartz expression)
    # BlockTime  = "54 59 08 * * ?"

    # Stop after syncing N blocks since startup
    # BlockCount = 12
  }
}

Only one of the three options can be set at a time. Setting multiple causes the node to fail to start with an error — which is why the other two are commented out above.

Once the node stops, you can back up output-directory or restart the node in query-only mode.

Query-only mode after shutdown

After the conditional shutdown, restart with --p2p-disable true to serve queries without rejoining P2P:

java -jar FullNode.jar -c config.conf --p2p-disable true

In this mode the node does not participate in peer discovery or block sync, but the HTTP and gRPC API surface continues to work — so you can query the snapshot state without it advancing. See Deploy a node — read-only query node.


Network stability and resource usage control

For nodes that face abnormal traffic or run on memory-constrained hardware, two operational levers help control resource consumption.

Cap JVM direct memory

Set the MaxDirectMemorySize JVM parameter to bound off-heap (direct) memory. The java-tron x86_64 startup script sets it to 10% of physical RAM, rounded down to whole GB, while the ARM64 startup script uses 1g by default. For example, on a 16 GB host:

# 16 GB host: 1 GB direct memory cap
java -XX:MaxDirectMemorySize=1g -jar FullNode.jar -c config.conf

Without this cap, direct memory can grow until the OS kills the process, producing hard-to-diagnose crashes.

Throttle TCP traffic with iptables

Use the kernel hashlimit module to bound inbound and outbound TCP rates. Two example rules — adjust the numeric thresholds to your bandwidth budget:

Outbound — limit per-destination-IP rate:

iptables -A OUTPUT -p tcp -m hashlimit \
  --hashlimit-name out_limit \
  --hashlimit-mode dstip \
  --hashlimit-above 15mb/sec \
  --hashlimit-burst 30mb \
  -j DROP

This caps outbound traffic to each destination IP at 15 MB/s steady-state with a 30 MB burst window.

Inbound — limit per-source-IP rate on the P2P port:

iptables -A INPUT -p tcp --dport 18888 -m hashlimit \
  --hashlimit-name in_limit \
  --hashlimit-mode srcip,dstport \
  --hashlimit-above 300/sec \
  --hashlimit-burst 600 \
  -j DROP

This caps incoming TCP packets to port 18888 (default P2P listen port) from any single source IP at 300 packets/sec with a 600-packet burst window.

hashlimit parameters:

ParameterMeaning
--hashlimit-nameInternal label so you can identify the rule
--hashlimit-modeBucket dimension: srcip, dstip, or combinations like srcip,dstport
--hashlimit-aboveSteady-state rate above which packets match
--hashlimit-burstBurst tolerance above the steady-state rate
-jAction to take when matched (commonly DROP)

different resultCode — sync stops mid-block

When tron.log contains different resultCode, the local node executed a transaction and produced a result different from what the block records. The node refuses to accept the block, halting sync.

The message is emitted from TransactionTrace.java:345. Older java-tron versions logged it from Manager.java, so existing logs may show different file:line locations — the message format (and the diagnosis) is stable. Three common patterns:

Expected: SUCCESS; Actual: OUT_OF_TIME

ERROR [sync-handle-block] different resultCode
  txId: ae53f8a6394d7adcc2337e0e71724f520818161cbb1f6f5d556f873b08e17c99,
  expect: SUCCESS, actual: OUT_OF_TIME

Cause: first inspect the transaction's specific OUT_OF_TIME message. If a protocol check triggered the result, verify the local node version first. If execution actually timed out, slower CPU or storage on the local node may cause the same transaction to exceed the time limit during local replay.

Fix: upgrade the node to the version required by the current network. If execution actually timed out, verify that the hardware meets the production recommendation. If the configuration is close to the minimum requirements, increase vm.maxTimeRatio in config.conf:

vm.maxTimeRatio = 50

Restart the node to apply.

Expected: OUT_OF_ENERGY; Actual: SUCCESS

ERROR [pool-48-thread-1] different resultCode
  txId: fbe7109a993b52243dc4de4087967cebc74be739d725dc27e96eb757496bd359,
  expect: OUT_OF_ENERGY, actual: SUCCESS

Cause: local databases are inconsistent — transactions that should have failed are marked as successful. Typically caused by an unclean shutdown, hardware fault, or other database corruption.

Fix: download the latest database snapshot, wipe output-directory, restore from the snapshot, and restart. Doing this re-grounds every database against a known-consistent baseline.

Expected: OUT_OF_TIME; Actual: SUCCESS

ERROR [sync-handle-block] different resultCode
  txId: 4ccf1feb7da348cef4190bc5d84d09d3c37160bfbdd21a0b5fd0b1d5005ead09,
  expect: OUT_OF_TIME, actual: SUCCESS

Cause: when the local node runs with --debug, it skips the CPU execution-time check. A transaction recorded as OUT_OF_TIME because its execution actually timed out may therefore return SUCCESS during local replay. OUT_OF_TIME results triggered by protocol checks are not affected by --debug.

Fix: remove --debug from the public-network node's start-up command and restart the node. If the result still differs, use the specific error message to verify the node version.


Bypass the transaction execution time limit in an isolated test environment

The TVM execution time limit for smart contract transactions is controlled by chain parameter #13, getMaxCpuTimeOfOneTx; its current Mainnet value is 80 ms. Use --debug only when testing or debugging a smart contract transaction that may exceed this limit on a fully isolated private chain or in a local environment that is not connected to a public network:

java -jar FullNode.jar -c config.conf --debug

With --debug enabled, the TVM no longer checks whether actual execution exceeds the CPU time limit. An OUT_OF_TIME explicitly triggered by protocol code still applies.

🚧

Warning

Do not enable --debug on a node connected to Mainnet, Nile, or any other public network. If a block records a transaction result as OUT_OF_TIME, the local execution result may differ from the block record, triggering different resultCode and stopping block synchronization. For causes and recovery steps, see different resultCode.

If you only need more time for simulated execution by triggerconstantcontract, estimateenergy, or similar requests, use vm.constantCallTimeoutMs instead of --debug. See the simulation timeout configuration guidance.

For instructions on creating an isolated environment, see TRON private chain.


Related resources