AlexxIT/go2rtc · error

: can't send command

Error message

%s: can't send command %d

What it means

WriteCommand retries sending a command frame a bounded number of times; when all attempts are exhausted without success it returns "cs2: can't send command %d" identifying the command code. It means the command could not be delivered/acknowledged within the retry budget, usually because the connection is dead or the peer is unresponsive.

Solutions

  1. Check the connection state (c.err) before calling WriteCommand; reconnect first if the worker has failed
  2. Increase the retry count / retry timeout passed to WriteCommand for slow links
  3. Verify the peer is online and responsive (ping via msgPing) before issuing the command
  4. Implement command idempotency so a safe retry after reconnect does not double-execute the command

Example fix

// before
if err := conn.WriteCommand(cmd, payload); err != nil {
    return fmt.Errorf("send failed: %w", err)
}
// after
if conn.Err() != nil {
    if err := conn.Reconnect(); err != nil {
        return fmt.Errorf("reconnect failed: %w", err)
    }
}
if err := conn.WriteCommand(cmd, payload); err != nil {
    return fmt.Errorf("send failed: %w", err)
}
Defensive patterns

Strategy: retry

Validate before calling

// verify connectivity before sending a command
func canSend(c *Conn) bool { return c != nil && c.Err() == nil }

Type guard

func sendable(c *Conn) bool { return c != nil && c.Err() == nil }

Try / catch

var err error
for i := 0; i < 3; i++ {
    if canSend(conn) { break }
    if err = conn.Reconnect(); err != nil { time.Sleep(backoff(i)); continue }
}
if err == nil { err = conn.WriteCommand(cmd, payload) }

Prevention

When it happens

Trigger: Calling WriteCommand (public) while the worker has exited due to a read error (c.err set), or the peer never consumes/acks the command before the repeat counter and timeout expire.

Common situations: Calling WriteCommand after the connection dropped; peer device offline or hung; retry count configured too low for a slow or lossy link.

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/1857a90aaaedeace. Report an issue: GitHub.

Appendix: source

Thrown at pkg/xiaomi/miss/cs2/conn.go:238

	timeout := time.NewTicker(time.Second)
	defer timeout.Stop()

	c.cmdAck = func() {
		repeat.Store(0)
		timeout.Reset(1)
	}

	for {
		if _, err := c.Conn.Write(req); err != nil {
			return err
		}
		<-timeout.C
		r := repeat.Add(-1)
		if r < 0 {
			return nil
		}
		if r == 0 {
			return fmt.Errorf("%s: can't send command %d", "cs2", cmd)
		}
	}
}

const hdrSize = 32

func (c *Conn) ReadPacket() (hdr, payload []byte, err error) {
	data, ok := c.channels[2].Pop()
	if !ok {
		return nil, nil, c.Error()
	}
	return data[:hdrSize], data[hdrSize:], nil
}

func (c *Conn) WritePacket(hdr, payload []byte) error {
	const offset = 12

	n := hdrSize + uint32(len(payload))

View on GitHub (pinned to c245815e75)