AlexxIT/go2rtc · error

speaker wait failed

Error message

speaker wait failed: %w

What it means

SendSpeaker publishes a speaker protocol message and then blocks on speakerWaiter.Wait(), which is completed by onMqttSpeaker when the camera responds. This error wraps any error the waiter was completed with — i.e. the camera replied with a non-zero resCode (delivered via error 316) or the wait timed out / was cancelled by the waiter implementation. It's the user-facing signal that the speaker command failed at the device level.

Solutions

  1. Unwrap the error to see the underlying cause (resCode value vs timeout) and handle each distinctly
  2. Increase/verify the waiter timeout to accommodate camera wake-up latency
  3. Ensure the camera is awake (call WakeUp first) and subscribed to response topics
  4. Check the speaker request payload parameters against what the device model supports

Example fix

// before
err := client.SendSpeaker(sdp, res, streamType, hevc)
// after
err := client.SendSpeaker(sdp, res, streamType, hevc)
if err != nil && strings.Contains(err.Error(), "speaker wait failed") {
    if strings.Contains(err.Error(), "resCode") { return err }
    // timeout path: retry once after waking the device
    _ = client.WakeUp(deviceID)
    err = client.SendSpeaker(sdp, res, streamType, hevc)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure device is awake before speaker commands
if !deviceAwake { if err := client.WakeUp(deviceID); err != nil { return err } }

Try / catch

err := client.SendSpeaker(...)
if err != nil {
    if strings.Contains(err.Error(), "resCode") { return fmt.Errorf("device rejected: %v", err) }
    // else treat as timeout: retry once
}

Prevention

When it happens

Trigger: Calling SendSpeaker when the camera responds with resCode != 0, or the waiter times out because no speaker response arrives (camera offline, wrong dps payload rejected), or the wait context is cancelled.

Common situations: Camera busy or in low-power sleep and not answering within the wait window, unsupported audio parameters in the speaker request, or MQTT responses arriving on a topic the client isn't subscribed to.

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

Appendix: source

Thrown at pkg/tuya/mqtt.go:263

	}

	return c.sendMqttMessage("resolution", 312, "", ResolutionFrame{
		Mode:  "webrtc",
		Value: resolution, // 0: HD, 1: SD
	})
}

func (c *TuyaMqttClient) SendSpeaker(speaker int) error {
	if err := c.sendMqttMessage("speaker", 312, "", SpeakerFrame{
		Mode:  "webrtc",
		Value: speaker, // 0: off, 1: on
	}); err != nil {
		return err
	}

	// Wait for camera response
	if err := c.speakerWaiter.Wait(); err != nil {
		return fmt.Errorf("speaker wait failed: %w", err)
	}

	return nil
}

func (c *TuyaMqttClient) SendDisconnect() error {
	return c.sendMqttMessage("disconnect", 302, "", DisconnectFrame{
		Mode: "webrtc",
	})
}

func (c *TuyaMqttClient) onConnect(client mqtt.Client) {
	if token := client.Subscribe(c.subscribeTopic, 1, c.onMessage); token.Wait() && token.Error() != nil {
		c.waiter.Done(token.Error())
		return
	}

	c.waiter.Done(nil)

View on GitHub (pinned to c245815e75)