hyperledger/fabric · error

can't read the block: %v

Error message

can't read the block: %v

What it means

The deliver service returned a status response instead of the requested block. readBlock expects a block, so any non-block status (e.g. FORBIDDEN, NOT_FOUND, BAD_REQUEST, SERVICE_UNAVAILABLE) is reported as "can't read the block" with the status detail.

Source

Thrown at internal/peer/common/deliverclient.go:75

func (d *DeliverClient) seekOldest() error {
	env := seekHelper(d.ChannelID, seekOldest, d.TLSCertHash, d.Signer, d.BestEffort)
	return d.Service.Send(env)
}

func (d *DeliverClient) seekNewest() error {
	env := seekHelper(d.ChannelID, seekNewest, d.TLSCertHash, d.Signer, d.BestEffort)
	return d.Service.Send(env)
}

func (d *DeliverClient) readBlock() (*cb.Block, error) {
	msg, err := d.Service.Recv()
	if err != nil {
		return nil, errors.Wrap(err, "error receiving")
	}
	switch t := msg.Type.(type) {
	case *ab.DeliverResponse_Status:
		logger.Infof("Expect block, but got status: %v", t)
		return nil, errors.Errorf("can't read the block: %v", t)
	case *ab.DeliverResponse_Block:
		logger.Infof("Received block: %v", t.Block.Header.Number)
		if resp, err := d.Service.Recv(); err != nil { // Flush the success message
			logger.Errorf("Failed to flush success message: %s", err)
		} else if status := resp.GetStatus(); status != cb.Status_SUCCESS {
			logger.Errorf("Expect status to be SUCCESS, got: %s", status)
		}

		return t.Block, nil
	default:
		return nil, errors.Errorf("response error: unknown type %T", t)
	}
}

// GetSpecifiedBlock gets the specified block from a peer/orderer's deliver
// service
func (d *DeliverClient) GetSpecifiedBlock(num uint64) (*cb.Block, error) {
	err := d.seekSpecified(num)

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Check the status embedded in the error (e.g. FORBIDDEN vs NOT_FOUND) and address that cause
  2. Verify the requested block number exists (ledger height) and hasn't been pruned
  3. Ensure the client signer has deliver/read permission on the channel
  4. Retry against a different orderer/peer serving the channel

Example fix

// before
block, err := client.GetSpecifiedBlock(num)
// after
block, err := client.GetSpecifiedBlock(num)
if err != nil && strings.Contains(err.Error(), "can't read the block") {
    log.Fatalf("deliver rejected, check status/ACLs: %v", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// check ledger height before seeking
info, _ := ledgerClient.QueryInfo()
if num >= info.Height { return errors.New("block number beyond ledger height") }
// ensure the identity has deliver/read rights on the channel

Type guard

if statusResp, ok := msg.Type.(*ab.DeliverResponse_Status); ok {
    return fmt.Errorf("deliver rejected with status %s", statusResp.Status)
}

Try / catch

block, err := client.GetSpecifiedBlock(num)
if err != nil && strings.Contains(err.Error(), "can't read the block") {
    // inspect embedded status: FORBIDDEN => ACL, NOT_FOUND => pruned/out-of-range
    return translateDeliverStatus(err)
}

Prevention

When it happens

Trigger: The deliver request was rejected by the server: seeking a block the server won't serve (out of range/pruned ledger), the identity lacks deliver/read ACLs, or the server reports internal unavailability.

Common situations: Requesting an old archived block that has been pruned, using a client identity not authorized for the deliver service, seeking a block number beyond the ledger height, orderer unavailable during maintenance.

Related errors


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