OpenNHP/opennhp · error

failed to unmarshal JSON

Error message

failed to unmarshal JSON %s to struct: %w

What it means

After successfully reading the file, LoadJsonFileAsStruct unmarshals it into map[string]any and wraps json.Unmarshal failures. It means the file content is not syntactically valid JSON (or, since it targets map[string]any, the top-level value is not a JSON object). The offending file content is embedded in the message for diagnosis.

Solutions

  1. Validate the file with `python3 -m json.tool <file>` or jq to see the exact syntax error and fix the content.
  2. Ensure the top-level JSON value is an object ({...}); wrap arrays/scalars into an object or change the loader.
  3. Regenerate the file programmatically with SaveStructAsJsonFile instead of hand-editing.
  4. If files are updated in place, write atomically (temp file + rename) so readers never see partial content; strip a UTF-8 BOM if present.

Example fix

// before
// file content: 'key = "value"'  (TOML, invalid JSON)
data, err := utils.LoadJsonFileAsStruct("etc/ta.toml")
// after
// file content: {"key": "value"}
data, err := utils.LoadJsonFileAsStruct("etc/ta.json")
Defensive patterns

Strategy: validation

Validate before calling

raw, err := os.ReadFile(path)
if err == nil && json.Valid(raw) {
    // safe to call LoadJsonFileAsStruct
}

Try / catch

data, err := utils.LoadJsonFileAsStruct(path)
var syn *json.SyntaxError
if errors.As(err, &syn) {
    return fmt.Errorf("invalid JSON at offset %d: %v", syn.Offset, syn)
}

Prevention

When it happens

Trigger: Calling LoadJsonFileAsStruct on a file containing TOML/YAML instead of JSON, an empty or truncated file, a JSON array or scalar at the top level (cannot unmarshal into map[string]any), or a partially written/corrupted file.

Common situations: An operator hand-edited the trust-anchor file and left a trailing comma or comments (invalid JSON); the file was saved by a different tool in TOML format; concurrent write truncated the file mid-update; encoding issues like BOM at the start of the file.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at nhp/utils/utils.go:146

	}

	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 {
		return nil, fmt.Errorf("failed to unmarshal JSON %s to struct: %w", string(jsonData), err)
	}

	return data, nil
}

func UpdateTomlConfig(filePath string, key string, value any) error {
	content, err := os.ReadFile(filePath)
	if err != nil {
		return err
	}

	var newContent string

	switch value := value.(type) {
	case string:
		re := regexp.MustCompile(`(?m)^\s*` + key + `\s*=\s*".+"\s*$`)
		newContent = re.ReplaceAllString(string(content), fmt.Sprintf("%s = \"%s\"", key, value))
	default:

View on GitHub (pinned to 6e04ca5ff0)