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.
| Symptom | Cause | Go to |
|---|---|---|
| Spendable below total, after sending | Outputs reserved by an in-flight transfer | Case 1 |
| Spendable below total, after mining | Coinbase maturity | Case 2 |
| A send appears to hang for minutes | Mempool confirmation wait | Case 3 |
| You sent and the recipient never received it | Delivery failure | Case 4 |
| Figures disagree with the chain | Local database out of date | Case 5 |
| You received and it never confirmed | Waiting on the sender | Case 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:
- Linux and macOS
- Windows
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
$secret = (Get-Content "$HOME\.epic\main\.api_secret" -First 1).Trim()
$body = '{"jsonrpc":"2.0","method":"get_status","params":[],"id":1}'
Invoke-RestMethod -Uri 'http://127.0.0.1:3413/v2/owner' -Method Post `
-ContentType 'application/json' -Body $body `
-Authentication Basic -Credential (
[PSCredential]::new('epic', (ConvertTo-SecureString $secret -AsPlainText -Force))
) -AllowUnencryptedAuthentication
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.