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
- Validate the file with `python3 -m json.tool <file>` or jq to see the exact syntax error and fix the content.
- Ensure the top-level JSON value is an object ({...}); wrap arrays/scalars into an object or change the loader.
- Regenerate the file programmatically with SaveStructAsJsonFile instead of hand-editing.
- 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
- Generate these files only via SaveStructAsJsonFile; never hand-edit in place.
- Write atomically (temp file + os.Rename) so readers never see partial content.
- Run `jq empty file` in CI/provisioning to validate JSON artifacts.
- Remember map[string]any requires a top-level JSON object; avoid arrays/scalars at the root.
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
- failed to unmarshal evidence
- cluster instance # : must set either Host or Ip
- cluster instance # : invalid port
- AuthServiceId is required
- json parsing error
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)