AlexxIT/go2rtc · error

speaker channel not connected

Error message

speaker channel not connected

What it means

WriteAudio sends encoded audio frames over the backchannel (speaker) channel. Before writing it checks conn.IsBackchannelReady(); if the intercom channel was never enabled (StartIntercom not called or not yet acknowledged), the write is refused with this error.

Solutions

  1. Call StartIntercom and wait until conn.IsBackchannelReady() (or intercom-ready signal) before writing audio.
  2. Gate the audio-send loop with a ready flag/condition variable instead of calling WriteAudio unconditionally.
  3. After any reconnect, re-run StartIntercom to re-enable the speaker channel.
  4. Buffer or drop outgoing frames while the backchannel is not ready instead of erroring per-frame.

Example fix

// before
client.WriteAudio(codec, payload, ts, 16000, 1)

// after
if client.IsBackchannelReady() {
    if err := client.WriteAudio(codec, payload, ts, 16000, 1); err != nil {
        log.Printf("write audio: %v", err)
    }
}
Defensive patterns

Strategy: type-guard

Validate before calling

if !client.IsBackchannelReady() {
    return fmt.Errorf("backchannel not ready; call StartIntercom first")
}

Type guard

func canWriteAudio(c *wyze.Client) bool { return c != nil && c.IsBackchannelReady() }

Try / catch

if err := client.WriteAudio(codec, payload, ts, rate, ch); err != nil {
    if strings.Contains(err.Error(), "speaker channel not connected") {
        // drop frame or re-enable intercom
        return errBackchannelDown
    }
    return err
}

Prevention

When it happens

Trigger: Calling WriteAudio without a prior successful StartIntercom; StartIntercom still in flight (backchannel not yet ready); backchannel dropped after a reconnect or camera-side channel close.

Common situations: Pushing microphone frames into WriteAudio as soon as a track is added, before the camera acknowledged the control-channel response; session reconnect cleared backchannel readiness; Opus/AAC frames queued during connection setup.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at pkg/wyze/client.go:242

func (c *Client) StopIntercom() error {
	if c.conn == nil || !c.conn.IsBackchannelReady() {
		return nil
	}

	k10010 := c.buildK10010(MediaTypeReturnAudio, false)
	c.conn.WriteIOCtrl(k10010)

	return c.conn.AVServStop()
}

func (c *Client) ReadPacket() (*tutk.Packet, error) {
	return c.conn.AVRecvFrameData()
}

func (c *Client) WriteAudio(codec byte, payload []byte, timestamp uint32, sampleRate uint32, channels uint8) error {
	if !c.conn.IsBackchannelReady() {
		return fmt.Errorf("speaker channel not connected")
	}

	if c.verbose {
		fmt.Printf("[Wyze] WriteAudio: codec=0x%02x, payload=%d bytes, rate=%d, ch=%d\n", codec, len(payload), sampleRate, channels)
	}

	return c.conn.AVSendAudioData(codec, payload, timestamp, sampleRate, channels)
}

func (c *Client) SetDeadline(t time.Time) error {
	if c.conn != nil {
		return c.conn.SetDeadline(t)
	}
	return nil
}

func (c *Client) Protocol() string {
	return "wyze/dtls"

View on GitHub (pinned to c245815e75)