hyperledger/fabric · error

transaction %d has no signature

Error message

transaction %d has no signature

What it means

VerifyTransactionsAreWellFormed requires every envelope in a block to carry a signature. If proto.Unmarshal succeeded but env.Signature is empty, the transaction is unsigned and cannot be authenticated, so the function rejects it. This mirrors the payload check and enforces the Envelope invariant that both Payload and Signature must be set.

Source

Thrown at protoutil/blockutils.go:338

	// If we have a single transaction, and the block is a config block, then no need to check
	// well formed-ness, because there cannot be another transaction in the original block.
	if HasConfigTx(bd) {
		return nil
	}

	for i, rawTx := range bd.Data {
		env := &cb.Envelope{}
		if err := proto.Unmarshal(rawTx, env); err != nil {
			return fmt.Errorf("transaction %d is invalid: %v", i, err)
		}

		if len(env.Payload) == 0 {
			return fmt.Errorf("transaction %d has no payload", i)
		}

		if len(env.Signature) == 0 {
			return fmt.Errorf("transaction %d has no signature", i)
		}

		expected, err := proto.Marshal(env)
		if err != nil {
			return fmt.Errorf("failed re-marshaling envelope: %v", err)
		}

		if len(expected) < len(rawTx) {
			return fmt.Errorf("transaction %d has %d trailing bytes", i, len(rawTx)-len(expected))
		}
		if !bytes.Equal(expected, rawTx) {
			return fmt.Errorf("transaction %d (%s) does not match its raw form (%s)", i,
				base64.StdEncoding.EncodeToString(expected), base64.StdEncoding.EncodeToString(rawTx))
		}
	}

	return nil
}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Ensure the envelope was signed before marshaling: set env.Signature from the signer over the serialized Payload.
  2. Verify the block source — if a peer produced this block, compare with other replicas to determine if data is corrupt.
  3. In test code, use protoutil helpers that build signed envelopes rather than hand-constructing &cb.Envelope{}.
  4. Reject unsigned envelopes at ingestion before they ever reach a block.

Example fix

// before
env := &cb.Envelope{Payload: payloadBytes}
// after
sig, err := signer.Sign(payloadBytes)
if err != nil { return err }
env := &cb.Envelope{Payload: payloadBytes, Signature: sig}
Defensive patterns

Strategy: validation

Validate before calling

env := &cb.Envelope{}
if err := proto.Unmarshal(rawTx, env); err != nil { return err }
if len(env.Signature) == 0 {
	return fmt.Errorf("tx %d: unsigned envelope", i)
}

Type guard

func isSigned(env *cb.Envelope) bool { return env != nil && len(env.Signature) > 0 }

Prevention

When it happens

Trigger: BlockDataHash encounters, at transaction index i, an Envelope whose Signature byte slice is empty — typically an envelope constructed programmatically without calling Sign, or one whose signature was stripped.

Common situations: Test fixtures building envelopes with only Payload set; code that re-marshals envelopes and drops the Signature field; envelopes deserialized from a truncated/corrupted block; forgetting env.Signature = signer.Sign(payload).

Related errors


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