{"record":{"id":"c79abf59a27db291","repo":"OpenNHP/opennhp","slug":"json-parsing-error-s-c79abf","errorCode":null,"errorMessage":"json parsing error: %s","messagePattern":"json parsing error: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"endpoints/server/msghandler.go","lineNumber":768,"sourceCode":"func ReadZdtoConfig(doId string) (common.DRGMsg, error) {\n\tetcDir := filepath.Join(ExeDirPath, \"etc\", \"ztdo\")\n\tconfigFilePath := filepath.Join(etcDir, \"data-\"+doId+\".json\")\n\tfile, err := os.Open(configFilePath)\n\tif err != nil {\n\t\treturn common.DRGMsg{}, fmt.Errorf(\"could not open file: %v\", err)\n\t}\n\tdefer file.Close()\n\n\tfileContentByte, err := io.ReadAll(file)\n\tif err != nil {\n\t\treturn common.DRGMsg{}, fmt.Errorf(\"error reading file: %v\", err)\n\t}\n\n\tvar config common.DRGMsg\n\n\terr = json.Unmarshal(fileContentByte, &config)\n\tif err != nil {\n\t\treturn common.DRGMsg{}, fmt.Errorf(\"json parsing error: %s\", err)\n\t}\n\treturn config, nil\n}\n\n// HandleRelayForward processes a decrypted NHP_RLY message from the standard\n// Noise pipeline.  The relay's identity has already been validated by\n// validatePeer as part of the standard decryption flow.\n//\n// The message body is a JSON-encoded RelayForwardMsg containing:\n//   - SourceAddr:  the real client's IP/port\n//   - InnerPacket: base64-encoded inner NHP packet (encrypted by agent)\n//\n// The inner packet is injected into the standard pipeline as if the agent\n// had connected directly.\nfunc (s *UdpServer) HandleRelayForward(ppd *core.PacketParserData) error {\n\tvar rlyMsg common.RelayForwardMsg\n\tif err := json.Unmarshal(ppd.BodyMessage, &rlyMsg); err != nil {\n\t\tlog.Error(\"server-relay[HandleRelayForward] failed to parse RelayForwardMsg: %v\", err)","sourceCodeStart":750,"sourceCodeEnd":786,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/server/msghandler.go#L750-L786","documentation":"ReadZdtoConfig read `etc/ztdo/data-<doId>.json` but json.Unmarshal could not parse its bytes into common.DRGMsg. The on-disk ZTDO config is malformed or its schema doesn't match DRGMsg fields, so the data-object config is unusable.","triggerScenarios":"HandleDHPDARMessage/HandleDHPDAVMessage/SaveZdtoConfig load a config file that was hand-edited, truncated by a crashed write, produced by a different (incompatible) schema version, or contains invalid JSON characters.","commonSituations":"Manual edits to data-*.json introducing syntax errors; server upgrade changed common.DRGMsg field types so old configs no longer unmarshal; disk-full partially wrote the file.","solutions":["Validate the file with a JSON linter / `jq . data-<doId>.json` and fix the syntax error it reports.","Compare the file's fields against the current common.DRGMsg struct and update the config to the current schema.","Delete the corrupt file and re-publish the ZTDO through the DHP DRG flow to regenerate it.","Restore the file from backup if it was truncated by a failed write."],"exampleFix":"// before: hand-edited file with trailing comma\n{\"doId\": \"abc\", \"accessUrl\": \"https://x\",}\n// after: valid JSON matching DRGMsg\n{\"doId\": \"abc\", \"accessUrl\": \"https://x\"}","handlingStrategy":"validation","validationCode":"p := filepath.Join(exeDir, \"etc\", \"ztdo\", \"data-\"+doId+\".json\")\nb, err := os.ReadFile(p)\nif err != nil { return err }\nif !json.Valid(b) {\n    return fmt.Errorf(\"%s is not valid JSON\", p)\n}\nvar probe map[string]any\nif err := json.Unmarshal(b, &probe); err != nil { return err }","typeGuard":"func isValidZtdoConfig(p string) bool {\n    b, err := os.ReadFile(p)\n    if err != nil { return false }\n    var m common.DRGMsg\n    return json.Unmarshal(b, &m) == nil\n}","tryCatchPattern":"msg, err := ReadZdtoConfig(doId)\nif err != nil && strings.Contains(err.Error(), \"json parsing error\") {\n    log.Warn(\"corrupt ztdo config %s: %v — re-publishing\", doId, err)\n    os.Remove(filepath.Join(ExeDirPath, \"etc\", \"ztdo\", \"data-\"+doId+\".json\"))\n    return republishZtdo(doId)\n}","preventionTips":["Never hand-edit data-*.json; always go through the DRG publish flow.","Run `jq .` validation after any out-of-band config restore.","Write configs atomically (temp file + rename) to avoid truncation on crash."],"tags":["json","config","parsing"],"backgroundTag":"json-parse-error","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}