hyperledger/fabric · critical

failed to read ledger directory

Error message

failed to read ledger directory %s

What it means

NewProvider for the file-based blockstore stats the chains directory (conf.getChainsDir(), typically under FileLedger/ledgerData). If os.Stat fails with anything other than NotExist — e.g. permission denied or an I/O error — the error is wrapped with this message and provider creation aborts. Note that NotExist is tolerated and the directory is created.

Solutions

  1. Run the wrapped cause (os.Stat error) to see the OS-level reason (permission denied vs I/O).
  2. Fix permissions on the ledger directory: chown/chmod so the peer process user has rwx.
  3. Check that the volume/mount is writable and not full (df, mount ro/rw).
  4. Verify the peer's fileLedger location config matches the directory actually provisioned.
  5. If migrating containers/k8s, align the securityContext runAsUser/FSGroup with the directory owner.

Example fix

// before: peer fails to start with unreadable ledger dir
// after: provision correct ownership before starting
// $ sudo chown -R 7051:7051 /var/hyperledger/production/ledgersData
// $ chmod -R u+rwX /var/hyperledger/production/ledgersData
Defensive patterns

Strategy: validation

Validate before calling

dirPath := filepath.Join(ledgerDataPath, "chains")
if fi, err := os.Stat(dirPath); err != nil && !os.IsNotExist(err) {
    return fmt.Errorf("cannot access ledger dir %s: %v — fix permissions/mount before starting peer", dirPath, err)
}

Try / catch

provider, err := blkstorage.NewProvider(conf)
if err != nil && strings.Contains(err.Error(), "failed to read ledger directory") {
    return fmt.Errorf("check ownership/permissions/mount of the ledger directory: %w", err)
}
if err != nil { return err }

Prevention

When it happens

Trigger: Starting the peer (or calling NewProvider/openBlockStorage) when the ledger directory exists but is unreadable: wrong ownership/permissions, an EACCES/EPERM/EIO from the filesystem, or the path being an unreadable mount.

Common situations: Peer running as non-root against a ledgerData dir owned by another user, read-only or full mount, container volume permission mismatch after image upgrade, Kubernetes securityContext UID changes.

Related errors


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

Appendix: source

Thrown at common/ledger/blkstorage/blockstore_provider.go:74

	stats           *stats
}

// NewProvider constructs a filesystem based block store provider
func NewProvider(conf *Conf, indexConfig *IndexConfig, metricsProvider metrics.Provider) (*BlockStoreProvider, error) {
	dbConf := &leveldbhelper.Conf{
		DBPath:         conf.getIndexDir(),
		ExpectedFormat: dataFormatVersion(indexConfig),
	}

	p, err := leveldbhelper.NewProvider(dbConf)
	if err != nil {
		return nil, err
	}

	dirPath := conf.getChainsDir()
	if _, err := os.Stat(dirPath); err != nil {
		if !os.IsNotExist(err) { // NotExist is the only permitted error type
			return nil, errors.Wrapf(err, "failed to read ledger directory %s", dirPath)
		}

		logger.Info("Creating new file ledger directory at", dirPath)
		if err = os.MkdirAll(dirPath, 0o755); err != nil {
			return nil, errors.Wrapf(err, "failed to create ledger directory: %s", dirPath)
		}
	}

	stats := newStats(metricsProvider)
	return &BlockStoreProvider{conf, indexConfig, p, stats}, nil
}

// Open opens a block store for given ledgerid.
// If a blockstore is not existing, this method creates one
// This method should be invoked only once for a particular ledgerid
func (p *BlockStoreProvider) Open(ledgerid string) (*BlockStore, error) {
	indexStoreHandle := p.leveldbProvider.GetDBHandle(ledgerid)
	return newBlockStore(ledgerid, p.conf, p.indexConfig, indexStoreHandle, p.stats)

View on GitHub (pinned to 2736b63f8f)