hyperledger/fabric · error
transaction invalidated with status (%s)
Error message
transaction invalidated with status (%s)
What it means
When the deliver service reports the transaction's validation code in a filtered block, any code other than VALID means the transaction was rejected by the committer. ClientWait surfaces the specific validation status in this error.
Source
Thrown at internal/peer/chaincode/common.go:739
// ClientWait waits for the specified deliver client to receive
// a block event with the requested txid
func (dg *DeliverGroup) ClientWait(dc *DeliverClient) {
defer dg.wg.Done()
for {
resp, err := dc.Connection.Recv()
if err != nil {
err = errors.WithMessagef(err, "error receiving from deliver filtered at %s", dc.Address)
dg.setError(err)
return
}
switch r := resp.Type.(type) {
case *pb.DeliverResponse_FilteredBlock:
filteredTransactions := r.FilteredBlock.FilteredTransactions
for _, tx := range filteredTransactions {
if tx.Txid == dg.TxID {
logger.Infof("txid [%s] committed with status (%s) at %s", dg.TxID, tx.TxValidationCode, dc.Address)
if tx.TxValidationCode != pb.TxValidationCode_VALID {
err = errors.Errorf("transaction invalidated with status (%s)", tx.TxValidationCode)
dg.setError(err)
}
return
}
}
case *pb.DeliverResponse_Status:
err = errors.Errorf("deliver completed with status (%s) before txid received", r.Status)
dg.setError(err)
return
default:
err = errors.Errorf("received unexpected response type (%T) from %s", r, dc.Address)
dg.setError(err)
return
}
}
}
// WaitForWG waits for the deliverGroup's wait group and closesView on GitHub (pinned to 2736b63f8f)
Solutions
- Read the status in the message (e.g. MVCC_READ_CONFLICT) and address the root cause — retry with fresh reads for MVCC conflicts
- Ensure unique txids (never reuse generated proposal IDs)
- Verify chaincode logic returns success and satisfies the endorsement policy before invoking
- Check peer logs for the exact validation failure details for the txid
Example fix
// before
resp, _ := contract.SubmitTransaction("put", key, value) // key hotly contended by many clients
// after
// retry with backoff on MVCC conflicts
for i := 0; i < 3; i++ {
resp, err := contract.SubmitTransaction("put", key, value)
if err == nil || !strings.Contains(fmt.Sprint(err), "MVCC_READ_CONFLICT") { break }
time.Sleep(time.Duration(1<<i) * 100 * time.Millisecond)
} Defensive patterns
Strategy: try-catch
Try / catch
if strings.Contains(err.Error(), "transaction invalidated with status") {
status := extractBetween(err.Error(), "status (", ")")
switch status {
case "MVCC_READ_CONFLICT":
return retryWithBackoff(invoke)
default:
return fmt.Errorf("tx rejected: %s — check endorsement policy and chaincode logs", status)
}
} Prevention
- Design keys/workloads to minimize concurrent writes to the same key
- Always generate fresh txids
- Validate chaincode logic and endorsement policies before deploying
When it happens
Trigger: An invoke whose endorsement was accepted and broadcast, but failed validation at commit — e.g. MVCC_READ_CONFLICT (concurrent writes to same key), endorsement policy failure, invalid chaincode status, or duplicate txid.
Common situations: High-contention workloads causing MVCC conflicts; submitting the same txid twice; chaincode returning an error that still produced an endorsed tx; endorsement policy not satisfied.
Related errors
- %s is mandatory and cannot be empty
- enrollment certificate isn't a valid PEM block
- failed to decode PEM block from %s
- could not create channel %s sub-group config
- config ID illegal, cannot be empty
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/617978ddcc219f6a.
Report an issue: GitHub.