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 closes

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Read the status in the message (e.g. MVCC_READ_CONFLICT) and address the root cause — retry with fresh reads for MVCC conflicts
  2. Ensure unique txids (never reuse generated proposal IDs)
  3. Verify chaincode logic returns success and satisfies the endorsement policy before invoking
  4. 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

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


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/617978ddcc219f6a. Report an issue: GitHub.