Skip to main content

Diagnose a stuck transaction

An incomplete transfer leaves funds either reserved by a transfer you can cancel, immature, or waiting to confirm. Nothing about an unfinished transfer consumes value.

First, classify it

epic-wallet info

Compare the figures. The gap between total and spendable tells you which case applies.

SymptomCauseGo to
Spendable below total, after sendingOutputs reserved by an in-flight transferCase 1
Spendable below total, after miningCoinbase maturityCase 2
A send appears to hang for minutesMempool confirmation waitCase 3
You sent and the recipient never received itDelivery failureCase 4
Figures disagree with the chainLocal database out of dateCase 5
You received and it never confirmedWaiting on the senderCase 6

Case 1: outputs reserved by an in-flight transfer

epic-wallet txs

Look for entries of type Sent (Created) with Confirmed? false, which the Owner API reports as tx_type: "TxSentCreated". Each one is holding inputs. The amount unavailable is larger than the amount you sent, because the change output is also unconfirmed until the transaction lands.

Decide whether the counterparty will respond. If they are offline and expected back, waiting is correct. If the transfer is not going to complete, cancel it:

epic-wallet cancel -i <id>

or by slate id:

epic-wallet cancel -t <slate_id>

Cancelling returns the reserved inputs to spendable and discards the unconfirmed change. Your balance updates immediately.

cancel has preconditions, and a transaction already posted to the mempool is past them. See cancelling has preconditions.

After cancelling, start a fresh send rather than reusing the old slate.

Case 2: coinbase maturity

Mined rewards cannot be spent until they reach the maturity depth for the network, which is 1,440 blocks on mainnet. See emission timing.

The amount shows as not currently spendable until that depth is reached.

Case 3: the send appears to hang

After posting a transaction, the wallet polls the node's mempool once per second for up to four minutes to confirm it arrived, printing nothing during that window. It is 240 attempts at one second apiece, in send (controller/src/command.rs:609), in finalize (controller/src/command.rs:779) and in the epicbox listener's own finalise path (impls/src/adapters/epicbox.rs:582). The Owner API calls behind those commands block for the same window, so a backend must not run them on a request thread.

Let it finish. If it ends with Transaction with slate_id ... not found in mempool after waiting, the post did not reach the node. Check the node is running and synced, then see Case 5.

Case 4: the recipient never received it

The transfer failed at delivery. What to check depends on the transport.

Over epicbox. The relay queues a slate for a recipient who is not connected. Two limits apply and the shorter one is not the storage expiry. A stored slate is removed after 7 days regardless of state. Separately, a recipient that connects repeatedly without acknowledging the slate it is offered has its whole pending queue discarded after nine delivery attempts, which at the default 60-second challenge interval is roughly nine minutes. The sender is not notified either way, so treat an accepted send as accepted for delivery rather than delivered. See delivery and its limits.

Ask the recipient to run epic-wallet listen -m epicbox and leave it running. If the slate is still queued it will arrive. If it was discarded, cancel and send again.

Over HTTP. There is no queue. The listener had to be reachable at the moment you sent. Cancel and retry when it is up.

Either way, check wallet versions. A 3.x wallet and a 4.x wallet can disagree about the slate format.

Case 5: local database out of date

If the wallet's figures disagree with the chain, rebuild its view:

epic-wallet scan

This rescans the chain against your keys and repairs the output set. It does not delete records.

It will not clear locked outputs, because a lock is a local record the chain knows nothing about. Use cancel for those.

If a rescan appears to do nothing, check the node's height first. When the node reports a height lower than the wallet's last confirmed height, the refresh is skipped, which is what you see against a node that is still syncing or has been reset:

curl -s -u epic:$(cat ~/.epic/main/.api_secret) -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"get_status","params":[],"id":1}' \
http://127.0.0.1:3413/v2/owner

The credential is covered in the credential the wallet presents to the node.

Case 6: received but never confirmed

As the receiver you completed your round and returned the slate. Finalising and posting is the sender's step, and you cannot do it for them, because you do not hold their keys.

epic-wallet txs

A Received entry with Confirmed? false means exactly this, tx_type: "TxReceived" through the API. Contact the sender.

TxReceived keeps its type after confirming, so confirmed is the only field that tells you. fee is empty on received transfers, because the receiver never set it.

To abandon it, cancel works on TxReceived as well, and releases only what your side reserved.

If none of these fit

Collect epic-wallet info, epic-wallet txs and the node's get_status from Case 5. Also note your wallet and node versions, the transport used, and whether the counterparty had a listener running.

Next