AlexxIT/go2rtc · warning

err.Error()

Error message

err.Error()

What it means

apiStream's /add branch decodes the request body as JSON into addJSON ({name, channels.0.url}); on decode failure it returns the Go JSON error with 400 Bad Request. The Hass rtsp-to-webrtc integration sends this payload, so any malformed or non-JSON body triggers it.

Solutions

  1. Send a JSON body matching {"name":"...","channels":{"0":{"url":"..."}}}
  2. Set Content-Type: application/json on the request
  3. Capture the raw body and validate it with a JSON linter before posting
  4. Check the rtsp_to_webrtc integration version matches the go2rtc API expected by your go2rtc version

Example fix

// before
curl -X POST http://go2rtc:1984/stream/x/add
// after
curl -X POST -H 'Content-Type: application/json' -d '{"name":"cam","channels":{"0":{"url":"rtsp://cam/stream"}}}' http://go2rtc:1984/stream/x/add
Defensive patterns

Strategy: validation

Validate before calling

const body = {name, channels: {0: {url}}};
const payload = JSON.stringify(body);
JSON.parse(payload); // sanity check serializable/valid
await fetch('/api/stream/x/add', {
  method:'POST',
  headers:{'Content-Type':'application/json'},
  body: payload
});

Try / catch

try {
  const res = await fetch(addUrl, opts);
  if (res.status === 400) console.error('add rejected:', await res.text());
} catch (e) {}

Prevention

When it happens

Trigger: POST to /stream/{id}/add with empty body, non-JSON content, or JSON missing the expected shape (name, channels["0"].url).

Common situations: Wrong Content-Type causing the client to send form data; older/newer Home Assistant rtsp_to_webrtc integration versions sending a different payload schema; manually curling the endpoint without a body.

Related errors


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

Appendix: source

Thrown at internal/hass/api.go:25

	"net/http"
	"strings"

	"github.com/AlexxIT/go2rtc/internal/api"
	"github.com/AlexxIT/go2rtc/internal/streams"
	"github.com/AlexxIT/go2rtc/internal/webrtc"
)

func apiOK(w http.ResponseWriter, r *http.Request) {
	api.Response(w, `{"status":1,"payload":{}}`, api.MimeJSON)
}

func apiStream(w http.ResponseWriter, r *http.Request) {
	switch {
	// /stream/{id}/add
	case strings.HasSuffix(r.RequestURI, "/add"):
		var v addJSON
		if err := json.NewDecoder(r.Body).Decode(&v); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		// we can get three types of links:
		// 1. link to go2rtc stream: rtsp://...:8554/{stream_name}
		// 2. static link to Hass camera
		// 3. dynamic link to Hass camera
		if _, err := streams.Patch(v.Name, v.Channels.First.Url); err == nil {
			apiOK(w, r)
		} else {
			http.Error(w, err.Error(), http.StatusBadRequest)
		}

	// /stream/{id}/channel/0/webrtc
	default:
		i := strings.IndexByte(r.RequestURI[8:], '/')
		if i <= 0 {
			http.Error(w, "", http.StatusBadRequest)

View on GitHub (pinned to c245815e75)