hyperledger/fabric · critical

failed to read ledger directory %s

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.

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)

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.