You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Transaction broadcast succeeded but was not included on-chain #6546
OS : Linux
JVM : Oracle Corporation 1.8.0_161 amd64
Version : 4.8.0
Expected behaviour
After a transaction is broadcast successfully, it should be packaged into a block and included on-chain normally.
Actual behaviour
An abnormal behavior was observed in a private network: transactions that were broadcast successfully were not included on-chain.
The private network consists of 1 SR node and 2 Fullnodes, all connected to each other.
The two Fullnodes were started at the same time and immediately began synchronizing blocks from the SR (both were in the block-catching-up state).
During the catch-up process, a transaction was broadcast to one of the Fullnodes:
The Fullnode returned a transaction broadcast success response.
However, the transaction never included on-chain, and it did not appear in any subsequent blocks.
Based on network logs alone, the transaction seemed to be received and propagated by the nodes during the catch-up period, but it was not selected by the SR for block production.
Once block syncing completed, the issue disappeared and the system returned to normal behaviour.
Frequency
This issue had not occurred previously. It was discovered during a recent private network deployment and testing.
@jakamobiii Based on your description, after the two fullnodes start up, they quickly complete block synchronization and enter broadcast state because their block heights are very close. Transactions can be broadcast between the two fullnodes, but since the fullnodes and the SR are in a synchronization state, transactions cannot be broadcast to the SR. Therefore, although the transaction appears to have been successfully broadcast, it is not actually recorded on the chain. It's best to wait until the fullnodes have completed synchronization before broadcasting the transaction.
@zeusoo001 Another question is whether the API interface needs optimization. It shows a successful broadcast, but the transaction isn't actually recorded on the blockchain.
This issue is included in the v4.8.2 release (ref: #6585). Development is currently in progress. We will continue to sync updates here as work advances. The fix is tentatively expected to be completed by April 2026.
Quick update: after reviewing the synchronization-state handling changes, we've decided to move this fix to the next release cycle instead of including it in v4.8.2.
Reason: although the implementation itself is relatively contained, the change affects transaction broadcast semantics and API behavior during node catch-up. We'd prefer to finalize the synchronization-threshold configuration and compatibility behavior more carefully rather than introducing it late in the current release cycle.
The fix direction has already been determined and implementation can continue in parallel for the next release window.
Status update: This feature request is no longer planned for the next release. We are removing the status: next-release label and keeping the issue in the backlog for future consideration.
Although the implementation itself is relatively contained, the change affects transaction broadcast semantics and API behavior during node catch-up. We'd prefer to finalize the synchronization-threshold configuration and compatibility behavior more carefully rather than introducing it late in the current release cycle.
We will revisit this once the design is better defined and the release scope allows it.
The catch-up explanation above is right; I won't repeat it. Worth adding is that it's one instance of a more general property, and that part of what was asked for already exists in the code.
What SUCCESS actually attests.Wallet.broadcastTransaction returns SUCCESS once the transaction has a valid signature, passes the duplicate check, enters the local pending pool, and is relayed to at least one peer. That is a local acceptance and relay receipt — it says nothing about whether a producer will see it, select it, or keep it.
Other cases where a successfully-broadcast transaction legitimately never lands, none involving a node being behind:
Expiration.Manager.validateCommon rejects a transaction once expiration <= headBlockTime. Anything waiting in the pool past its expiration window is dropped — by design.
Production budget. In generateBlock, once System.currentTimeMillis() > timeout the producer stops packing; the rest of the pending queue isn't in that block.
Block size cap. A transaction that doesn't fit the remaining BLOCK_SIZE is postponed.
Multi-sign cap. At most one multi-sign transaction per owner address goes into a given block.
Fork switch. A transaction in a block that later gets orphaned returns to the re-push queue and may expire before it is packed again.
In practice 2–5 combine with 1: a transaction is deferred for a few blocks, then dies of expiration. Inclusion was never guaranteed in the first place.
What would be a defect. A fully synced node with healthy connections to a producing SR broadcasts a transaction well within its expiration window, not a duplicate, hitting none of the caps above — and it still never appears in any block across many production slots, while demonstrably present in the producer's pending pool. That is worth reporting. Everything above it is the protocol
working as designed.
The originally reported scenario therefore looks like expected behaviour. What's still genuinely open is the narrower question of whether SUCCESS should ever be returned by a node that knows it cannot reach a producer.
@SeriousCoding789, I agree that SUCCESS means the node accepted the transaction; it does not guarantee on-chain inclusion. A transaction may still expire or be deferred by block production limits, so a successful broadcast that is never included is not, by itself, a bug. This issue focuses on broadcast feedback while a node is catching up: we want to identify cases where the transaction cannot be effectively propagated and reduce the chance of returning SUCCESS when it cannot reach a producing node. This improvement would not guarantee eventual inclusion.
Activity
zeusoo001 commentedon Feb 5, 2026
@jakamobiii Based on your description, after the two fullnodes start up, they quickly complete block synchronization and enter broadcast state because their block heights are very close. Transactions can be broadcast between the two fullnodes, but since the fullnodes and the SR are in a synchronization state, transactions cannot be broadcast to the SR. Therefore, although the transaction appears to have been successfully broadcast, it is not actually recorded on the chain. It's best to wait until the fullnodes have completed synchronization before broadcasting the transaction.
317787106 commentedon Feb 5, 2026
@jakamobiii You should not broadcast any transaction through FullNode until it catched up the latest block.
lxcmyf commentedon Feb 5, 2026
@jakamobiii
In the node synchronization phase, although the transaction is broadcast, it does not flow to SR, so it is not linked.
jakamobiii commentedon Feb 5, 2026
Besides waiting for block synchronization to complete, are there other solutions?
zeusoo001 commentedon Feb 5, 2026
@jakamobiii It's also possible to broadcast through other nodes that have completed block synchronization.
jakamobiii commentedon Feb 5, 2026
@zeusoo001 Another question is whether the API interface needs optimization. It shows a successful broadcast, but the transaction isn't actually recorded on the blockchain.
zeusoo001 commentedon Feb 5, 2026
@jakamobiii Yes, that's a very good question. I think it's necessary to optimize it. You can follow the progress of this issue.
zeusoo001 commentedon Mar 19, 2026
This issue is included in the v4.8.2 release (ref: #6585). Development is currently in progress. We will continue to sync updates here as work advances. The fix is tentatively expected to be completed by April 2026.
xxo1shine commentedon May 15, 2026
Quick update: after reviewing the synchronization-state handling changes, we've decided to move this fix to the next release cycle instead of including it in v4.8.2.
Reason: although the implementation itself is relatively contained, the change affects transaction broadcast semantics and API behavior during node catch-up. We'd prefer to finalize the synchronization-threshold configuration and compatibility behavior more carefully rather than introducing it late in the current release cycle.
The fix direction has already been determined and implementation can continue in parallel for the next release window.
xxo1shine commentedon Sep 3, 2026
Status update: This feature request is no longer planned for the next release. We are removing the status: next-release label and keeping the issue in the backlog for future consideration.
Although the implementation itself is relatively contained, the change affects transaction broadcast semantics and API behavior during node catch-up. We'd prefer to finalize the synchronization-threshold configuration and compatibility behavior more carefully rather than introducing it late in the current release cycle.
We will revisit this once the design is better defined and the release scope allows it.
SeriousCoding789 commentedon Sep 22, 2026
The catch-up explanation above is right; I won't repeat it. Worth adding is that it's one instance of a more general property, and that part of what was asked for already exists in the code.
What SUCCESS actually attests.
Wallet.broadcastTransactionreturns SUCCESS once the transaction has a valid signature, passes the duplicate check, enters the local pending pool, and is relayed to at least one peer. That is a local acceptance and relay receipt — it says nothing about whether a producer will see it, select it, or keep it.Other cases where a successfully-broadcast transaction legitimately never lands, none involving a node being behind:
Manager.validateCommonrejects a transaction onceexpiration <= headBlockTime. Anything waiting in the pool past its expiration window is dropped — by design.generateBlock, onceSystem.currentTimeMillis() > timeoutthe producer stops packing; the rest of the pending queue isn't in that block.BLOCK_SIZEis postponed.In practice 2–5 combine with 1: a transaction is deferred for a few blocks, then dies of expiration. Inclusion was never guaranteed in the first place.
What would be a defect. A fully synced node with healthy connections to a producing SR broadcasts a transaction well within its expiration window, not a duplicate, hitting none of the caps above — and it still never appears in any block across many production slots, while demonstrably present in the producer's pending pool. That is worth reporting. Everything above it is the protocol
working as designed.
The originally reported scenario therefore looks like expected behaviour. What's still genuinely open is the narrower question of whether SUCCESS should ever be returned by a node that knows it cannot reach a producer.
xxo1shine commentedon Sep 22, 2026
@SeriousCoding789, I agree that SUCCESS means the node accepted the transaction; it does not guarantee on-chain inclusion. A transaction may still expire or be deferred by block production limits, so a successful broadcast that is never included is not, by itself, a bug. This issue focuses on broadcast feedback while a node is catching up: we want to identify cases where the transaction cannot be effectively propagated and reduce the chance of returning SUCCESS when it cannot reach a producing node. This improvement would not guarantee eventual inclusion.