hyperledger/fabric · error
no ledger context: %s %s %+v
Error message
no ledger context: %s %s %+v
What it means
isValidTxSim looks up the TransactionContext for a (channelID, txid) pair and requires that a TXSimulator is attached. The error means the chaincode issued a ledger read/write message (GetState, PutState, etc.) for a txid with no registered transaction simulator — i.e., the message arrived outside a valid transaction context, or after the context was deleted (e.g., post-commit or in Init without simulator). The formatted message includes channel, txid, and underlying error details.
Source
Thrown at core/chaincode/handler.go:589
}
func (h *Handler) Notify(msg *pb.ChaincodeMessage) {
tctx := h.TXContexts.Get(msg.ChannelId, msg.Txid)
if tctx == nil {
chaincodeLogger.Debugf("notifier Txid:%s, channelID:%s does not exist for handling message %s", msg.Txid, msg.ChannelId, msg.Type)
return
}
chaincodeLogger.Debugf("[%s] notifying Txid:%s, channelID:%s", shorttxid(msg.Txid), msg.Txid, msg.ChannelId)
tctx.ResponseNotifier <- msg
tctx.CloseQueryIterators()
}
// is this a txid for which there is a valid txsim
func (h *Handler) isValidTxSim(channelID string, txid string, fmtStr string, args ...any) (*TransactionContext, error) {
txContext := h.TXContexts.Get(channelID, txid)
if txContext == nil || txContext.TXSimulator == nil {
err := errors.Errorf(fmtStr, args...)
chaincodeLogger.Errorf("no ledger context: %s %s\n\n %+v", channelID, txid, err)
return nil, err
}
return txContext, nil
}
// register Txid to prevent overlapping handle messages from chaincode
func (h *Handler) registerTxid(msg *pb.ChaincodeMessage) bool {
// Check if this is the unique state request from this chaincode txid
if h.ActiveTransactions.Add(msg.ChannelId, msg.Txid) {
return true
}
// Log the issue and drop the request
chaincodeLogger.Errorf("[%s] Another request pending for this CC: %s, Txid: %s, ChannelID: %s. Cannot process.", shorttxid(msg.Txid), h.chaincodeID, msg.Txid, msg.ChannelId)
return false
}
View on GitHub (pinned to 2736b63f8f)
Solutions
- Ensure all ledger reads/writes happen inside the single chaincode Invoke/Init call, before it returns
- Do not spawn goroutines that use the tx simulator after the transaction completes; re-invoke instead
- Verify the txid/channel passed to HandleTransaction matches an active (not yet deleted) transaction context
- Do not call GetState/PutState from Init if a simulator is unavailable; defer state writes to Invoke
Example fix
// before: goroutine uses simulator after tx ends
go func() { stub.PutState("k", v) }()
// after: keep ledger ops synchronous inside Invoke
func (c *CC) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
if err := stub.PutState("k", []byte("v")); err != nil { return shim.Error(err.Error()) }
return shim.Success(nil)
} Defensive patterns
Strategy: try-catch
Try / catch
val, err := stub.GetState(key)
if err != nil {
if strings.Contains(err.Error(), "no ledger context") {
// tx context gone: retry via a NEW transaction invocation, not with same txid
return shim.Error("transaction context expired; re-invoke")
}
return shim.Error(err.Error())
} Prevention
- Never use the tx simulator from goroutines that may outlive the Invoke/Init call
- Keep all ledger access synchronous inside the handler returning the response
- Don't call state APIs during Init unless a simulator is guaranteed
- In long-running chaincode work, return quickly and re-invoke for the rest
When it happens
Trigger: Chaincode calls GetState/PutState/DelState after the transaction has completed or been cleaned up; calling state APIs from a goroutine that outlives the transaction; passing a wrong txid through HandleTransaction/getTxContextForInvoke; calling query APIs during Init when no simulator exists.
Common situations: Long-running chaincode goroutines or timers that touch the ledger after the tx ends; retrying a request with an expired txid; invoking state APIs inside Init; channel/txid mismatches in custom peer-to-chaincode plumbing.
Related errors
- failed to find ledger for channel: %s
- error decoding the block number
- error decoding the data hash
- error decoding the previous hash
- error decoding the length of block data
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/bf01ae6c8ceec9d8.
Report an issue: GitHub.