OpenNHP/opennhp · error
already exists, please delete it first
Error message
%v already exists, please delete it first
What it means
SaveZdtoConfig found that `etc/ztdo/data-<doId>.json` still exists after the attempted removal, so writing would overwrite an existing ZTDO config. It refuses with "... already exists, please delete it first" rather than silently clobbering the config.
Solutions
- Delete etc/ztdo/data-<doId>.json manually and retry the DRG flow.
- Avoid sending concurrent DHP DRG messages for the same doId, or serialize them on the server.
- Check file permissions — if os.Remove failed silently due to permissions, fix directory writability first.
- Re-run after ensuring only one writer touches the ztdo directory.
Example fix
// before: race leaves the file behind
// two HandleDHPDRGMessage calls for doId X run concurrently
// after: serialize per-doId saves
var ztdoMu sync.Mutex
func SaveZdtoConfig(d *common.DRGMsg) error { ztdoMu.Lock(); defer ztdoMu.Unlock(); return saveZdtoLocked(d) } Defensive patterns
Strategy: try-catch
Validate before calling
p := filepath.Join(exeDir, "etc", "ztdo", "data-"+doId+".json")
if _, err := os.Stat(p); err == nil {
os.Remove(p) // deliberate replace: remove before save
} Type guard
func ztdoExists(doId string) bool {
_, err := os.Stat(filepath.Join(ExeDirPath, "etc", "ztdo", "data-"+doId+".json"))
return err == nil
} Try / catch
if err := SaveZdtoConfig(drg); err != nil && strings.Contains(err.Error(), "already exists") {
os.Remove(filepath.Join(ExeDirPath, "etc", "ztdo", "data-"+drg.DoId+".json"))
err = SaveZdtoConfig(drg)
} Prevention
- Serialize saves per doId (mutex or single-writer queue) to avoid remove/create races.
- Never hand-edit data-*.json while the server is running.
- Clean stale configs during maintenance windows instead of relying on overwrite.
When it happens
Trigger: HandleDHPDRGMessage calls SaveZdtoConfig for a doId whose config file exists but could not be read by ReadZdtoConfig (so the merge/remove branch at line 718 was skipped), yet os.Remove in the earlier branch didn't delete it — typically because the file was recreated between the ReadZdtoConfig failure and the os.Stat check, or the remove failed silently while the stat still sees it.
Common situations: Concurrent DHP DRG messages for the same doId arriving in parallel: one instance recreates the file after the other's remove; a corrupted data-<doId>.json that fails ReadZdtoConfig's JSON parse also blocks the remove-and-merge path under races.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- could not open file
- failed to create config.json
- could not open file
- error reading file
- file path cannot be empty
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/064f783f2d1da7d5.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/server/msghandler.go:735
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 Object
func ReadZdtoConfig(doId string) (common.DRGMsg, error) {
etcDir := filepath.Join(ExeDirPath, "etc", "ztdo")
configFilePath := filepath.Join(etcDir, "data-"+doId+".json")
file, err := os.Open(configFilePath)View on GitHub (pinned to 6e04ca5ff0)