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
- Check the status embedded in the error (e.g. FORBIDDEN vs NOT_FOUND) and address that cause
- Verify the requested block number exists (ledger height) and hasn't been pruned
- Ensure the client signer has deliver/read permission on the channel
- 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
- Never seek pruned or out-of-range block numbers
- Grant the client identity deliver/read privileges
- Check server status codes embedded in the error before retrying
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
- deliver completed with status (%s) before txid received
- timed out waiting for connection to deliver on all peers
- timed out waiting for txid on all peers
- received unexpected response type (%T) from %s
- proto: Marshal called with nil
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/d8c44712860b6b8a.
Report an issue: GitHub.