AlexxIT/go2rtc · error

discovery timeout

Error message

discovery timeout

What it means

discovery waits for the camera's discovery response (cmdDiscoRes) on the LAN/broadcast channel; if it never arrives, DialDTLS fails with this error. It means the host could not discover the camera before giving up, so no session address was learned.

Solutions

  1. Verify the device UID/identifier is correct
  2. Ensure host and camera are on the same L2 network or that UDP discovery relays are available
  3. Power-cycle the camera and retry DialDTLS
  4. Check for client/AP isolation or firewall rules blocking the discovery packets
Defensive patterns

Strategy: retry

Validate before calling

// precondition checks before DialDTLS
if deviceUID == "" || !sameSubnet(hostIP, camSubnet) {
	return fmt.Errorf("discovery cannot reach camera network")
}

Try / catch

conn, err := dtls.DialDTLS(ctx, uid, psk)
if err != nil {
	if strings.Contains(err.Error(), "discovery timeout") {
		// power-cycle/ping camera, then retry once
	}
	return err
}

Prevention

When it happens

Trigger: DialDTLS -> discovery when no UDP packet with cmdDiscoRes at offset 8 is received within the discovery window — camera offline, wrong network/broadcast, or discovery response dropped.

Common situations: Camera on a different subnet/VLAN than the host; camera asleep or just powered on; mDNS/UDP broadcast blocked by AP isolation; wrong device UID leading to no responder.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


AI-assisted analysis of AlexxIT/go2rtc@c245815e75 (2026-09-07). Data as JSON: /api/errors/95600bd5a3c4dfcc. Report an issue: GitHub.

Appendix: source

Thrown at pkg/tutk/dtls/conn_dtls.go:541

			if binary.LittleEndian.Uint16(buf[4:]) == cmdDiscoCC51 {
				c.addr, c.isCC51, c.ticket = addr, true, binary.LittleEndian.Uint16(buf[14:])
				if n >= 24 {
					copy(c.sid, buf[16:24])
				}
				return c.discoDoneCC51()
			}
			continue
		}

		// IOTC Protocol (Basis)
		data := tutk.ReverseTransCodeBlob(buf[:n])
		if len(data) >= 16 && binary.LittleEndian.Uint16(data[8:]) == cmdDiscoRes {
			c.addr, c.isCC51 = addr, false
			return c.discoDone()
		}
	}

	return fmt.Errorf("discovery timeout")
}

func (c *DTLSConn) discoDone() error {
	c.Write(c.msgDisco(2))
	time.Sleep(100 * time.Millisecond)
	_, err := c.WriteAndWait(c.msgSession(), func(res []byte) bool {
		return len(res) >= 16 && binary.LittleEndian.Uint16(res[8:]) == cmdSessionRes
	})
	return err
}

func (c *DTLSConn) discoDoneCC51() error {
	_, err := c.WriteAndWait(c.msgDiscoCC51(2, c.ticket, false), func(res []byte) bool {
		if len(res) < packetSizeCC51 || string(res[:2]) != magicCC51 {
			return false
		}
		cmd := binary.LittleEndian.Uint16(res[4:])
		dir := binary.LittleEndian.Uint16(res[8:])

View on GitHub (pinned to c245815e75)