OpenNHP/opennhp · error

json parsing error

Error message

json parsing error: %s

What it means

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.

Solutions

  1. Validate the file with a JSON linter / `jq . data-<doId>.json` and fix the syntax error it reports.
  2. Compare the file's fields against the current common.DRGMsg struct and update the config to the current schema.
  3. Delete the corrupt file and re-publish the ZTDO through the DHP DRG flow to regenerate it.
  4. Restore the file from backup if it was truncated by a failed write.

Example fix

// before: hand-edited file with trailing comma
{"doId": "abc", "accessUrl": "https://x",}
// after: valid JSON matching DRGMsg
{"doId": "abc", "accessUrl": "https://x"}
Defensive patterns

Strategy: validation

Validate before calling

p := filepath.Join(exeDir, "etc", "ztdo", "data-"+doId+".json")
b, err := os.ReadFile(p)
if err != nil { return err }
if !json.Valid(b) {
    return fmt.Errorf("%s is not valid JSON", p)
}
var probe map[string]any
if err := json.Unmarshal(b, &probe); err != nil { return err }

Type guard

func isValidZtdoConfig(p string) bool {
    b, err := os.ReadFile(p)
    if err != nil { return false }
    var m common.DRGMsg
    return json.Unmarshal(b, &m) == nil
}

Try / catch

msg, err := ReadZdtoConfig(doId)
if err != nil && strings.Contains(err.Error(), "json parsing error") {
    log.Warn("corrupt ztdo config %s: %v — re-publishing", doId, err)
    os.Remove(filepath.Join(ExeDirPath, "etc", "ztdo", "data-"+doId+".json"))
    return republishZtdo(doId)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


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

Appendix: source

Thrown at endpoints/server/msghandler.go:768

func ReadZdtoConfig(doId string) (common.DRGMsg, error) {
	etcDir := filepath.Join(ExeDirPath, "etc", "ztdo")
	configFilePath := filepath.Join(etcDir, "data-"+doId+".json")
	file, err := os.Open(configFilePath)
	if err != nil {
		return common.DRGMsg{}, fmt.Errorf("could not open file: %v", err)
	}
	defer file.Close()

	fileContentByte, err := io.ReadAll(file)
	if err != nil {
		return common.DRGMsg{}, fmt.Errorf("error reading file: %v", err)
	}

	var config common.DRGMsg

	err = json.Unmarshal(fileContentByte, &config)
	if err != nil {
		return common.DRGMsg{}, fmt.Errorf("json parsing error: %s", err)
	}
	return config, nil
}

// HandleRelayForward processes a decrypted NHP_RLY message from the standard
// Noise pipeline.  The relay's identity has already been validated by
// validatePeer as part of the standard decryption flow.
//
// The message body is a JSON-encoded RelayForwardMsg containing:
//   - SourceAddr:  the real client's IP/port
//   - InnerPacket: base64-encoded inner NHP packet (encrypted by agent)
//
// The inner packet is injected into the standard pipeline as if the agent
// had connected directly.
func (s *UdpServer) HandleRelayForward(ppd *core.PacketParserData) error {
	var rlyMsg common.RelayForwardMsg
	if err := json.Unmarshal(ppd.BodyMessage, &rlyMsg); err != nil {
		log.Error("server-relay[HandleRelayForward] failed to parse RelayForwardMsg: %v", err)

View on GitHub (pinned to 6e04ca5ff0)