OpenNHP/opennhp · error

failed to write JSON to file

Error message

failed to write JSON to file: %w

What it means

SaveStructAsJsonFile marshals any value to indented JSON and writes it with os.WriteFile. This error wraps the os.WriteFile failure, so the JSON was produced successfully but persisting it to filePath failed (bad directory, permissions, disk full, path is a directory). The underlying OS error is preserved via %w for errors.Is/As inspection.

Solutions

  1. Run os.MkdirAll(filepath.Dir(filePath), 0755) before calling SaveStructAsJsonFile.
  2. Inspect the wrapped error with errors.Is(err, fs.ErrNotExist) / fs.ErrPermission to pinpoint the OS cause.
  3. Verify the process user has write access to the target directory (ls -ld, check umask).
  4. Check disk space and mount flags (df -h, mount | grep ro) if path and permissions look correct.

Example fix

// before
if err := utils.SaveStructAsJsonFile("etc/ta/ta.json", taData); err != nil { return err }
// after
if err := os.MkdirAll("etc/ta", 0o755); err != nil { return err }
if err := utils.SaveStructAsJsonFile("etc/ta/ta.json", taData); err != nil { return err }
Defensive patterns

Strategy: try-catch

Validate before calling

if dir := filepath.Dir(path); dir != "" {
    if err := os.MkdirAll(dir, 0o755); err != nil { return err }
}
if err := os.WriteFile(path, []byte("probe"), 0o644); err != nil { /* surface before real write */ }

Try / catch

err := utils.SaveStructAsJsonFile(path, data)
if err != nil {
    var pe *fs.PathError
    if errors.As(err, &pe) && errors.Is(pe.Err, fs.ErrNotExist) {
        os.MkdirAll(filepath.Dir(path), 0o755)
        err = utils.SaveStructAsJsonFile(path, data)
    }
    if err != nil { return fmt.Errorf("persist %s: %w", path, err) }
}

Prevention

When it happens

Trigger: Calling SaveStructAsJsonFile(filePath, data) where filePath's parent directory does not exist, the process lacks write permission on the target path, the path points to a directory, or the filesystem is full/read-only.

Common situations: registerTAService tries to persist trust-anchor data before its config directory has been created (e.g. fresh deployment missing ~/nhp or ./etc); running the daemon as a non-root user after files were first written by root; container volumes mounted read-only; misspelled relative paths.

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

Appendix: source

Thrown at nhp/utils/utils.go:127

	return tempPath, nil
}

func SaveStructAsJsonFile(filePath string, data any) error {
	if data == nil {
		return fmt.Errorf("data cannot be nil")
	}
	if filePath == "" {
		return fmt.Errorf("file path cannot be empty")
	}

	jsonData, err := json.MarshalIndent(data, "", "  ")
	if err != nil {
		return fmt.Errorf("failed to marshal data to JSON: %w", err)
	}

	err = os.WriteFile(filePath, jsonData, 0644) //nolint:gosec // G306: Generic utility - callers determine sensitivity
	if err != nil {
		return fmt.Errorf("failed to write JSON to file: %w", err)
	}

	return nil
}

func LoadJsonFileAsStruct(filePath string) (any, error) {
	if filePath == "" {
		return nil, fmt.Errorf("file path cannot be empty")
	}

	jsonData, err := os.ReadFile(filePath)
	if err != nil {
		return nil, fmt.Errorf("failed to read file: %w", err)
	}

	var data map[string]any

	if err := json.Unmarshal(jsonData, &data); err != nil {

View on GitHub (pinned to 6e04ca5ff0)