OpenNHP/opennhp · error

error reading file

Error message

error reading file: %v

What it means

ReadZdtoConfig opened `etc/ztdo/data-<doId>.json` but io.ReadAll failed while reading its contents. The wrapped OS error (commonly EIO from failing disk or I/O on a network volume) prevents loading the DRGMsg.

Solutions

  1. Check the wrapped OS error (dmesg / storage health) and repair the underlying disk or remount the volume.
  2. Verify data-<doId>.json is a regular file, not a device/FIFO, and replace it if corrupted.
  3. Re-publish the ZTDO via the DRG flow to rewrite the config from source.
  4. If on network storage, ensure the mount is healthy before retrying.

Example fix

// before: no file-type check
file, _ := os.Open(configFilePath)
// after: ensure a regular file before reading
fi, _ := file.Stat()
if !fi.Mode().IsRegular() {
    return common.DRGMsg{}, fmt.Errorf("%s is not a regular file", configFilePath)
}
Defensive patterns

Strategy: fallback

Validate before calling

p := filepath.Join(exeDir, "etc", "ztdo", "data-"+doId+".json")
fi, err := os.Stat(p)
if err == nil && !fi.Mode().IsRegular() {
    return fmt.Errorf("%s is not a regular file", p)
}

Type guard

func isReadableRegularFile(p string) bool {
    fi, err := os.Stat(p)
    return err == nil && fi.Mode().IsRegular()
}

Try / catch

msg, err := ReadZdtoConfig(doId)
if err != nil && strings.Contains(err.Error(), "error reading file") {
    log.Error("ztdo IO failure: %v", err) // fallback to cache/re-publish
    return loadFromBackupOrRepublish(doId)
}

Prevention

When it happens

Trigger: HandleDHPDARMessage/HandleDHPDAVMessage or SaveZdtoConfig trigger ReadZdtoConfig on a config file whose read fails — rare, typically hardware/IO errors, detached network storage, or file changed to a special file (device/FIFO).

Common situations: etc/ztdo lives on an NFS/EBS volume that dropped; underlying disk errors; someone replaced the JSON file with a named pipe or device node.

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/0ac4059e980b164b. Report an issue: GitHub.

Appendix: source

Thrown at endpoints/server/msghandler.go:761

	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()

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

	var config common.DRGMsg

	err = json.Unmarshal(fileContentByte, &config)
	if err != nil {
		return common.DRGMsg{}, fmt.Errorf("json parsing error: %s", err)
	}
	return config, nil
}

// HandleRelayForward processes a decrypted NHP_RLY message from the standard
// Noise pipeline.  The relay's identity has already been validated by
// validatePeer as part of the standard decryption flow.
//
// The message body is a JSON-encoded RelayForwardMsg containing:
//   - SourceAddr:  the real client's IP/port
//   - InnerPacket: base64-encoded inner NHP packet (encrypted by agent)

View on GitHub (pinned to 6e04ca5ff0)