OpenNHP/opennhp · error
failed to open file
Error message
failed to open file: %v
What it means
NewDataPrivateKeyStoreWith loads a stored data private key file for a ZTDO from `<exeDir>/etc/ztdo/data-key-<doId>.json`. When os.Open fails - because the key file does not exist, the path is wrong, or the process lacks read permission - the store cannot be built and this wrapped error is returned to the caller (HandleUdpDataKeyWrappingOperations). The wrapped os error names the exact cause (e.g. 'no such file or directory').
Solutions
- Check the wrapped os error: if it is 'no such file or directory', verify the file exists at <exeDir>/etc/ztdo/data-key-<doId>.json and that the doId is correct.
- Create the key first by constructing a store with NewDataPrivateKeyStore(...), calling Generate(mode), then Save(doId) before loading it.
- Verify common.ExeDirPath matches where the keys were originally saved; restart the daemon from the expected directory or move the etc/ztdo directory.
- Fix file permissions (chmod/chown) so the process user can read the key file.
Example fix
// before: assumes the key file exists
store, err := db.NewDataPrivateKeyStoreWith(doId)
if err != nil { return err }
// after: generate and save on first use
store, err := db.NewDataPrivateKeyStoreWith(doId)
if err != nil {
if os.IsNotExist(errors.Unwrap(err)) {
store = db.NewDataPrivateKeyStore(providerPubB64)
store.Generate(ztdolib.ECCModeSm2)
if err := store.Save(doId); err != nil { return err }
} else {
return err
}
} Defensive patterns
Strategy: try-catch
Validate before calling
path := filepath.Join(common.ExeDirPath, "etc/ztdo", "data-key-"+doId+".json")
if _, err := os.Stat(path); os.IsNotExist(err) {
// key not yet created; generate and save first
} Type guard
func keyFileExists(doId string) bool {
p := filepath.Join(common.ExeDirPath, "etc/ztdo", "data-key-"+doId+".json")
st, err := os.Stat(p)
return err == nil && !st.IsDir()
} Try / catch
store, err := db.NewDataPrivateKeyStoreWith(doId)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
// fall back to generating a new key
} else {
return fmt.Errorf("loading data key for %s: %w", doId, err)
}
} Prevention
- Always generate and Save the key before any code path that loads it.
- Use absolute paths (resolve common.ExeDirPath once at startup) so cwd changes cannot hide key files.
- Log the resolved full path in the error to make doId/path mismatches obvious.
- Mount etc/ztdo as a persistent volume in containerized deployments.
When it happens
Trigger: Calling NewDataPrivateKeyStoreWith(doId) when: (1) the key file was never created via Save() for that doId; (2) the file was deleted or the doId string is mistyped so the filename data-key-<doId>.json does not match; (3) the daemon's working/executable directory changed so common.ExeDirPath-based relative path etc/ztdo no longer resolves; (4) file permissions deny read access.
Common situations: Fresh deployment where keys were generated on another machine; running nhp-db from a different cwd or container path than when keys were saved; UUID/doId mismatch between caller and stored file; volume not mounted in Docker.
Understand the failure class
Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.
Related errors
- already exists, please delete it first
- could not open file
- resource not found
- Error: fail to generating temporary file path
- error reading file
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/a329c80526655b82.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/db/utils.go:42
// NewDataPrivateKeyStore create a new DataPrivateKeyStore
func NewDataPrivateKeyStore(providerPublicKeyBase64 string) *DataPrivateKeyStore {
return &DataPrivateKeyStore{
ProviderPublicKeyBase64: providerPublicKeyBase64,
}
}
// 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()
}View on GitHub (pinned to 6e04ca5ff0)