OpenNHP/opennhp · error

failed to marshal data to JSON

Error message

failed to marshal data to JSON: %w

What it means

After input validation, SaveStructAsJsonFile marshals the data with json.MarshalIndent; if marshaling fails (e.g. unsupported types like channels, funcs, or cyclic data), the error is wrapped as 'failed to marshal data to JSON: %w' preserving the underlying cause. The file is not written in this case.

Solutions

  1. Read the wrapped %w cause to find the offending field/type
  2. Remove or replace unserializable fields (channels, funcs, cycles)
  3. Add json:"-" tags on fields that must not be serialized
  4. Pre-flight with json.Marshal in tests covering the payload type

Example fix

// before
type TA struct{ Stop chan struct{} }
SaveStructAsJsonFile(path, ta)
// after
type TA struct {
    Stop chan struct{} `json:"-"`
}
SaveStructAsJsonFile(path, ta)
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := json.Marshal(data); err != nil { return fmt.Errorf("payload unserializable: %w", err) }

Try / catch

var serr *json.UnsupportedTypeError
if err := SaveStructAsJsonFile(path, data); err != nil {
    if errors.As(err, &serr) { log.Error("field type not serializable: %v", serr.Value) }
    return err
}

Prevention

When it happens

Trigger: Passing data containing unserializable values: channels, complex, func values, NaN/Inf floats, cyclic references, or a custom MarshalJSON that errors.

Common situations: Struct fields holding time.Ticker channels or function pointers added during refactors; plugin payloads with opaque fields; map[string]any filled from external sources containing unsupported values.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


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

Appendix: source

Thrown at nhp/utils/utils.go:122

	if err := file.Close(); err != nil {
		return "", err
	}

	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)

View on GitHub (pinned to 6e04ca5ff0)