OpenNHP/opennhp · error
failed to create etc directory
Error message
failed to create etc directory: %v
What it means
SaveZdtoConfig could not create the ZTDO config directory `<ExeDirPath>/etc/ztdo`. os.MkdirAll failed (permissions, read-only filesystem, or a non-directory file at the path), so the data-<doId>.json config cannot be written and the DRG message handling fails.
Solutions
- Ensure the directory containing the nhp-serverd binary is writable by the process user (chown/chmod or run under the service account that owns it).
- If ExeDirPath/e tc exists as a file, remove or rename it so MkdirAll can create the directories.
- In containers, mount a writable volume at the server's working/exe directory or disable the read-only rootfs.
- Check SELinux/AppArmor policies that may block directory creation and add appropriate rules.
Example fix
// before: binary installed root-owned, server runs as nhp sudo chown -R nhp:nhp /opt/opennhp // after: or pre-create the writable ztdo dir sudo mkdir -p /opt/opennhp/etc/ztdo && sudo chown nhp:nhp /opt/opennhp/etc/ztdo
Defensive patterns
Strategy: validation
Validate before calling
dir := filepath.Join(exeDir, "etc", "ztdo")
if fi, err := os.Stat(dir); err == nil && !fi.IsDir() {
return fmt.Errorf("%s exists and is not a directory", dir)
}
if err := os.MkdirAll(dir, 0o755); err != nil {
return fmt.Errorf("cannot create %s: %v", dir, err)
} Type guard
func ztdoDirWritable() bool {
d := filepath.Join(ExeDirPath, "etc", "ztdo")
os.MkdirAll(d, 0o755)
p := filepath.Join(d, ".writetest")
if err := os.WriteFile(p, nil, 0o644); err != nil { return false }
os.Remove(p)
return true
} Try / catch
if err := publishZtdo(drgMsg); err != nil && strings.Contains(err.Error(), "failed to create etc directory") {
// fix perms or storage, then retry
fixStoragePermissions(); err = publishZtdo(drgMsg)
} Prevention
- Run the daemon under a user that owns its install/exe directory.
- Provision etc/ztdo in deployment scripts or mount a writable volume in containers.
- Avoid read-only rootfs without an explicit writable volume for state.
When it happens
Trigger: HandleDHPDRGMessage invokes SaveZdtoConfig while the server process lacks write permission on ExeDirPath, ExeDirPath is read-only (container rootfs), or a regular file named `etc` or `etc/ztdo` already exists blocking directory creation.
Common situations: Running nhp-serverd as a non-root user against a root-owned install directory; Kubernetes/Docker container with a read-only filesystem and no writable volume mounted at the exe path; accidental file created at the etc path.
Understand the failure class
Background: mkdir permission denied (EACCES): failed to create directory errors explained — this error's family across 32 libraries.
Related errors
- failed to create etc directory
- failed to create file
- keystore: create directory
- failed to open file
- resource not found
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/874ac39bf31225c6.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/server/msghandler.go:731
configFileName := "data-" + objectId + ".json"
etcDir := filepath.Join(ExeDirPath, "etc", "ztdo")
configPath := filepath.Join(etcDir, configFileName)
if existingDrgMsg, err := ReadZdtoConfig(objectId); err == nil {
// alway keep original date source type
drgMsg.DataSourceType = existingDrgMsg.DataSourceType
if drgMsg.AccessUrl == "" { // provider update access url
drgMsg.AccessUrl = existingDrgMsg.AccessUrl
}
os.Remove(configPath)
}
// Make sure the etc directory exists
if err := os.MkdirAll(etcDir, 0755); err != nil {
return fmt.Errorf("failed to create etc directory: %v", err)
}
if _, err := os.Stat(configPath); err == nil {
return fmt.Errorf("%v already exists, please delete it first", configFileName)
}
file, err := os.Create(configPath)
if err != nil {
return fmt.Errorf("failed to create config.json: %v", err)
}
defer file.Close()
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
return encoder.Encode(drgMsg)
}
// read data-<doId>.json to DRGMsg ObjectView on GitHub (pinned to 6e04ca5ff0)