OpenNHP/opennhp · error

failed to create config.json

Error message

failed to create config.json: %v

What it means

SaveZdtoConfig could not create (or truncate) the file `etc/ztdo/data-<doId>.json`. os.Create returned an error — the message text says "failed to create config.json" but it wraps the OS error from creating the ztdo data config. The DRG update is aborted.

Solutions

  1. Inspect the wrapped %v OS error and fix the cause (disk full: free space; permission: chown/chmod etc/ztdo).
  2. Confirm the filesystem containing ExeDirPath is writable at write time (mount options, rw remount).
  3. Validate the doId — reject/control excessively long or filesystem-illegal characters before persisting configs.
  4. Retry the DHP DRG operation once storage is healthy.

Example fix

// before: no guard on doId characters
configFileName := "data-" + objectId + ".json"
// after: sanitize before building the path
safeId := strings.Map(func(r rune) rune { if r=='/'||r=='\\'||r==0 {return -1}; return r }, objectId)
configFileName := "data-" + safeId + ".json"
Defensive patterns

Strategy: try-catch

Validate before calling

dir := filepath.Join(exeDir, "etc", "ztdo")
os.MkdirAll(dir, 0o755)
if err := syscall.Access(dir, syscall.O_RDWR); err != nil {
    return fmt.Errorf("ztdo dir not writable: %v", err)
}

Type guard

func canCreateIn(dir string) bool {
    p := filepath.Join(dir, ".probe")
    f, err := os.Create(p)
    if err != nil { return false }
    f.Close(); os.Remove(p)
    return true
}

Try / catch

if err := SaveZdtoConfig(drg); err != nil && strings.Contains(err.Error(), "failed to create config.json") {
    log.Error("ztdo write failed: %v — check disk space and permissions", err)
    freeDiskOrRemount(); err = SaveZdtoConfig(drg)
}

Prevention

When it happens

Trigger: HandleDHPDRGMessage → SaveZdtoConfig calls os.Create on a path in an unwritable directory, the disk is full, the path exceeds filesystem name limits (very long doId), or the name is invalid for the filesystem.

Common situations: Disk-full on the server volume hosting etc/ztdo; read-only mount after the earlier MkdirAll check raced with a remount; a doId containing characters illegal on the target filesystem.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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

Appendix: source

Thrown at endpoints/server/msghandler.go:740

		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)
	if err != nil {
		return common.DRGMsg{}, fmt.Errorf("could not open file: %v", err)
	}
	defer file.Close()

View on GitHub (pinned to 6e04ca5ff0)