OpenNHP/opennhp · error

missing source address

Error message

missing source address

What it means

HandleRelayForward rejected a relayed NHP_RLY message because its JSON RelayForwardMsg carried no SourceAddr. Without the real client's IP/port the server cannot attribute or validate the forwarded inner packet, so it drops the message with "missing source address".

Solutions

  1. Update the relay to a version that always populates RelayForwardMsg.SourceAddr with the real client's IP/port.
  2. Confirm the JSON field name/tag matches between relay and server (go vet / compare common.RelayForwardMsg on both sides).
  3. Check that the agent-to-relay path preserves the original source address metadata.
  4. If a hostile relay is suspected, verify relay identity/allow-lists and drop traffic from it.

Example fix

// before: relay omits source address
fwd := common.RelayForwardMsg{InnerPacket: b64}
// after: relay always sets the real client address
fwd := common.RelayForwardMsg{SourceAddr: &common.SourceAddr{Ip: clientIP, Port: clientPort}, InnerPacket: b64}
Defensive patterns

Strategy: type-guard

Validate before calling

var fwd common.RelayForwardMsg
if err := json.Unmarshal(body, &fwd); err != nil { return err }
if fwd.SourceAddr == nil || net.ParseIP(fwd.SourceAddr.Ip) == nil || fwd.SourceAddr.Port <= 0 {
    return fmt.Errorf("relay forward missing/invalid source address")
}

Type guard

func hasValidSourceAddr(m *common.RelayForwardMsg) bool {
    return m != nil && m.SourceAddr != nil &&
        net.ParseIP(m.SourceAddr.Ip) != nil &&
        m.SourceAddr.Port > 0 && m.SourceAddr.Port < 65536
}

Try / catch

if err := server.HandleRelayForward(ppd); err != nil && err.Error() == "missing source address" {
    log.Warn("relay %s sent forward without SourceAddr — check relay version", ppd.ConnData.RemoteAddr)
    // drop packet; optionally alert on the relay
}

Prevention

When it happens

Trigger: A relay forwards a JSON body whose `sourceAddr` field is null/omitted — either the relay's forwarder code didn't populate it, the agent's original packet lacked it, or a field-name mismatch (wrong JSON tag) caused unmarshal to leave the pointer nil.

Common situations: Mismatched relay/server versions where the relay builds RelayForwardMsg with an older field name; a misbehaving or malicious relay sending empty forward frames; agents talking through a relay that lost the original connection metadata.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at endpoints/server/msghandler.go:792

// 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)
		return err
	}

	if rlyMsg.SourceAddr == nil {
		log.Error("server-relay[HandleRelayForward] missing source address")
		return fmt.Errorf("missing source address")
	}

	innerBytes, err := base64.StdEncoding.DecodeString(rlyMsg.InnerPacket)
	if err != nil {
		log.Error("server-relay[HandleRelayForward] failed to decode inner packet: %v", err)
		return err
	}

	realIP := net.ParseIP(rlyMsg.SourceAddr.Ip)
	if reason := validateRelaySourceAddr(realIP, rlyMsg.SourceAddr.Port, s.allowPrivateRelaySource.Load()); reason != "" {
		log.Warning("server-relay[HandleRelayForward] rejecting %s from relay %s: %s:%d",
			reason, ppd.ConnData.RemoteAddr.String(), rlyMsg.SourceAddr.Ip, rlyMsg.SourceAddr.Port)
		return fmt.Errorf("%s relay source address", reason)
	}
	realAddr := &net.UDPAddr{IP: realIP, Port: rlyMsg.SourceAddr.Port}

	relayAddrStr := ppd.ConnData.RemoteAddr.String()
	log.Info("server-relay[HandleRelayForward] from relay %s, real client %s, inner %d bytes",

View on GitHub (pinned to 6e04ca5ff0)