hyperledger/fabric · error

failed reading directory

Error message

failed reading directory %s

What it means

ListInstalledChaincodes scans the peer's chaincode install directory with ls(dir) to enumerate installed chaincode packages. If the directory exists (os.Stat passed) but cannot be read — permissions, I/O error, or it is not actually a directory — the read error is wrapped with this message.

Solutions

  1. Fix filesystem permissions so the peer process can read the chaincode install directory
  2. Confirm the configured install path is a real directory ('ls -ld') and not a file or broken symlink/mount
  3. Check the wrapped underlying error for I/O details (EACCES, ENOTDIR, etc.)
  4. Restart or repair storage backing the peer's data directory if the filesystem is damaged

Example fix

// before
sudo chmod 700 /var/hyperledger/production/chaincodes
// after
sudo chown -R peer:peer /var/hyperledger/production/chaincodes && sudo chmod 755 /var/hyperledger/production/chaincodes
Defensive patterns

Strategy: try-catch

Validate before calling

dir := "/var/hyperledger/production/chaincodes"
fi, err := os.Stat(dir)
if err != nil || !fi.IsDir() {
    return fmt.Errorf("invalid chaincode dir %s", dir)
}
if _, err := os.ReadDir(dir); err != nil {
    return fmt.Errorf("cannot read chaincode dir: %w", err)
}

Type guard

func isReadableDir(path string) bool {
    fi, err := os.Stat(path)
    return err == nil && fi.IsDir()
}

Try / catch

chaincodes, err := ccprovider.ListInstalledChaincodes()
if err != nil {
    if strings.Contains(err.Error(), "failed reading directory") {
        // recover: fix permissions or recreate dir, then retry once
        return retryAfterDirRepair(err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling ListInstalledChaincodes when ls(dir) returns an error, e.g. the chaincode install path is unreadable by the peer process or is a file rather than a directory.

Common situations: Directory permissions changed after security hardening; the install path misconfigured to a file or mount point that disappeared; disk/NFS errors on the peer host.

Related errors


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

Appendix: source

Thrown at core/common/ccprovider/ccprovider.go:203

	return cccdspack, nil
}

// DirEnumerator enumerates directories
type DirEnumerator func(string) ([]os.DirEntry, error)

// ChaincodeExtractor extracts chaincode from a given path
type ChaincodeExtractor func(ccNameVersion string, path string, getHasher GetHasher) (CCPackage, error)

// ListInstalledChaincodes retrieves the installed chaincodes
func (cifs *CCInfoFSImpl) ListInstalledChaincodes(dir string, ls DirEnumerator, ccFromPath ChaincodeExtractor) ([]chaincode.InstalledChaincode, error) {
	var chaincodes []chaincode.InstalledChaincode
	if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
		return nil, nil
	}
	files, err := ls(dir)
	if err != nil {
		return nil, errors.Wrapf(err, "failed reading directory %s", dir)
	}

	for _, f := range files {
		// Skip directories, we're only interested in normal files
		if f.IsDir() {
			continue
		}
		// A chaincode file name is of the type "name.version"
		// We're only interested in the name.
		// Skip files that don't adhere to the file naming convention of "A.B"
		i := strings.Index(f.Name(), ".")
		if i == -1 {
			ccproviderLogger.Info("Skipping", f.Name(), "because of missing separator '.'")
			continue
		}
		ccName := f.Name()[:i]      // Everything before the separator
		ccVersion := f.Name()[i+1:] // Everything after the separator

View on GitHub (pinned to 2736b63f8f)