hyperledger/fabric · error
query iterator not found
Error message
query iterator not found
What it means
Raised in HandleQueryStateNext when txContext.GetQueryIterator(id) returns nil, meaning no query iterator is registered in the transaction context under the given iterator ID. The handler cannot continue an iteration that was never created or has already been cleaned up.
Source
Thrown at core/chaincode/handler.go:900
txContext.CleanupQueryContext(iterID)
return nil, errors.Wrap(err, "marshal failed")
}
chaincodeLogger.Debugf("Got keys and values. Sending %s", pb.ChaincodeMessage_RESPONSE)
return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: payloadBytes, Txid: msg.Txid, ChannelId: msg.ChannelId}, nil
}
// Handles query to ledger for query state next
func (h *Handler) HandleQueryStateNext(msg *pb.ChaincodeMessage, txContext *TransactionContext) (*pb.ChaincodeMessage, error) {
queryStateNext := &pb.QueryStateNext{}
err := proto.Unmarshal(msg.Payload, queryStateNext)
if err != nil {
return nil, errors.Wrap(err, "unmarshal failed")
}
queryIter := txContext.GetQueryIterator(queryStateNext.Id)
if queryIter == nil {
return nil, errors.New("query iterator not found")
}
totalReturnLimit := h.calculateTotalReturnLimit(nil)
payload, err := h.QueryResponseBuilder.BuildQueryResponse(txContext, queryIter, queryStateNext.Id, false, totalReturnLimit)
if err != nil {
txContext.CleanupQueryContext(queryStateNext.Id)
return nil, errors.WithStack(err)
}
if payload == nil {
txContext.CleanupQueryContext(queryStateNext.Id)
return nil, errors.New("marshal failed: proto: Marshal called with nil")
}
payloadBytes, err := proto.Marshal(payload)
if err != nil {
txContext.CleanupQueryContext(queryStateNext.Id)
return nil, errors.Wrap(err, "marshal failed")View on GitHub (pinned to 2736b63f8f)
Solutions
- Complete iterator consumption (Next/Close) within a short window, well inside the peer's queryIterator TTL.
- Never call Next after Close; check for closed iterators in chaincode code.
- Reduce long-running transactions or increase iterator TTL (ledger state query limit settings) on the peer.
- If the ID is genuinely stale, treat as a lifecycle bug: recreate the original query (GetQueryResult) instead of continuing.
Example fix
// chaincode-side before
iter, _ := stub.GetStateByRange(start, end)
// ...long processing...
iter.HasNext() // peer may have expired iterator
// after: consume and close promptly
iter, _ := stub.GetStateByRange(start, end)
for iter.HasNext() {
kv, _ := iter.Next()
process(kv)
}
iter.Close() Defensive patterns
Strategy: validation
Validate before calling
// chaincode-side: ensure the iterator is used before it expires and not after Close
if iter == nil {
return shim.Error("iterator missing: re-run the original query")
} Try / catch
iter, err := stub.GetStateByRange(start, end)
if err != nil { return shim.Error(err.Error()) }
defer iter.Close()
for iter.HasNext() {
kv, err := iter.Next()
if err != nil {
if strings.Contains(err.Error(), "query iterator not found") {
// iterator expired on peer; restart the query
return shim.Error("iterator expired; re-issue query")
}
return shim.Error(err.Error())
}
} Prevention
- Consume and close iterators promptly, inside one transaction
- Do not hold iterators across long computations or sleeps
- Avoid calling Next after Close
- Tune peer queryIterator TTL for large result processing
When it happens
Trigger: The chaincode sends a QueryStateNext message with an iterator ID that does not exist in txContext — typically after the iterator's TTL elapsed, after the query context was cleaned up, or when the ID was never created by a prior GetQueryResult/QueryStateNext response.
Common situations: Chaincode holds a state-query iterator across a long transaction while the peer's iterator TTL expires; calling Next after Close; parallel transactions reusing iterator IDs incorrectly; peer restart clearing in-memory iterator state.
Related errors
- application config does not exist for channel '%s'
- unknown chaincode '%s' for channel '%s'
- could not find chaincode with package id '%s'
- requested sequence is 0, but first definable sequence number
- currently defined sequence %d is larger than requested seque
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/db0a495c4b6deb36.
Report an issue: GitHub.