OpenNHP/opennhp · error

error reading file

Error message

error reading file: %v

What it means

After successfully opening the data private key file in NewDataPrivateKeyStoreWith, io.ReadAll streams its content into memory. If the read fails mid-stream - typically I/O errors on the underlying descriptor - the function aborts with this wrapped error instead of returning a partially initialized store.

Solutions

  1. Inspect the wrapped os error and check disk/storage health of the volume containing etc/ztdo (dmesg, smartctl, mount status).
  2. Retry the load; transient I/O errors often clear after storage recovers.
  3. If the file is corrupt/unreadable, delete it, regenerate the key (Generate + Save) and re-register the ZTDO key with the provider.
  4. Ensure the process has adequate file descriptors (ulimit -n) if many files are open concurrently.

Example fix

// before: hard failure with no recovery
store, err := db.NewDataPrivateKeyStoreWith(doId)
if err != nil { log.Fatalf("%v", err) }
// after: bounded retry on transient I/O errors
var store *db.DataPrivateKeyStore
for i := 0; i < 3; i++ {
	store, err = db.NewDataPrivateKeyStoreWith(doId)
	if err == nil { break }
	time.Sleep(time.Second)
}
Defensive patterns

Strategy: retry

Validate before calling

f, err := os.Open(path)
if err != nil { return err }
info, err := f.Stat()
if err == nil && !info.Mode().IsRegular() { err = errors.New("not a regular file") }
f.Close()

Type guard

func isTransientIO(err error) bool {
	return errors.Is(err, syscall.EIO) || errors.Is(err, syscall.EAGAIN) || errors.Is(err, syscall.EINTR)
}

Try / catch

var store *db.DataPrivateKeyStore
var err error
for attempt := 0; attempt < 3; attempt++ {
	store, err = db.NewDataPrivateKeyStoreWith(doId)
	if err == nil || !isTransientIO(errors.Unwrap(err)) { break }
	time.Sleep(500 * time.Millisecond << attempt)
}

Prevention

When it happens

Trigger: Calling NewDataPrivateKeyStoreWith(doId) when the data-key-<doId>.json file becomes unreadable after opening: disk I/O error, file removed while reading on some systems, descriptor exhaustion, or hardware/NFS failure during read.

Common situations: Failing disk or full/under-provisioned network volume hosting etc/ztdo; NFS/ESXi stale handle after the storage backend re-mounted; running in a container whose writable layer hit an I/O error.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07). Data as JSON: /api/errors/03325a04ba911cab. Report an issue: GitHub.

Appendix: source

Thrown at endpoints/db/utils.go:47

	}
}

// NewDataPrivateKeyStoreWith create a new DataPrivateKeyStore with doId
func NewDataPrivateKeyStoreWith(doId string) (d *DataPrivateKeyStore, err error) {
	etcDir := "etc/ztdo"
	fileName := "data-key-" + doId + ".json"

	fullPath := filepath.Join(common.ExeDirPath, etcDir, fileName)

	// open and read all the content in file
	file, err := os.Open(fullPath)
	if err != nil {
		return nil, fmt.Errorf("failed to open file: %v", err)
	}

	fileContentByte, err := io.ReadAll(file)
	if err != nil {
		return nil, fmt.Errorf("error reading file: %v", err)
	}

	d = &DataPrivateKeyStore{}
	_ = d.fromJson(fileContentByte)

	return
}

func (d *DataPrivateKeyStore) Generate(mode ztdolib.DataKeyPairECCMode) (privateKey []byte) {
	ecdh := core.NewECDH(mode.ToEccType())
	d.DataPrivateKeyBase64 = ecdh.PrivateKeyBase64()
	return ecdh.PrivateKey()
}

// Save saves the dataPrivateKeyBase64 to a file, the format of file name is data-<doId>.json
// Notes: this default way to store data private key is not safe. In the wild environment, need to use a secure way to store data private key.
func (d *DataPrivateKeyStore) Save(doId string) error {
	// Make sure the etc directory exists

View on GitHub (pinned to 6e04ca5ff0)