AlexxIT/go2rtc · error

err.Error()

Error message

err.Error()

What it means

When Content-Type is application/json, outputWebRTC decodes the request body into a pion.SessionDescription. If the body is not valid JSON (or not shaped like a session description), json.Decode fails and the handler logs the error and returns HTTP 400 with the decoder's error text. This guards the JSON SDP-exchange path against malformed payloads.

Solutions

  1. Send the body as {"type":"offer","sdp":"<full SDP>"} when using Content-Type: application/json
  2. Or drop the JSON Content-Type and send raw SDP (application/sdp) so the default branch reads the body
  3. Validate the JSON body client-side before sending

Example fix

// before
fetch(url, {method:'POST', headers:{'Content-Type':'application/json'}, body: pc.localDescription.sdp})
// after
fetch(url, {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(pc.localDescription)})
Defensive patterns

Strategy: validation

Validate before calling

const payload = {type: pc.localDescription.type, sdp: pc.localDescription.sdp};
JSON.parse(JSON.stringify(payload)); // throws early if not serializable
if (!payload.sdp?.startsWith('v=0')) throw new Error('invalid SDP');

Type guard

const isSessionDescription = (d) => d && typeof d.sdp === 'string' && d.sdp.startsWith('v=0');

Try / catch

try { ... } catch (e) { if (e.response?.status === 400) { console.error('Body must be valid JSON SessionDescription'); } }

Prevention

When it happens

Trigger: POST with Content-Type: application/json but body is raw SDP text instead of {"type":"offer","sdp":"v=0\r\n..."}; truncated body; sending SDP with the JSON content type; invalid JSON syntax.

Common situations: Clients that read the SDP from a file and forget to wrap it in the JSON envelope; copy-paste errors adding trailing text; a fetch() call whose body was serialized twice or not at all.

Related errors


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

Appendix: source

Thrown at internal/webrtc/server.go:86

	if stream == nil {
		http.Error(w, api.StreamNotFound, http.StatusNotFound)
		return
	}

	mediaType := r.Header.Get("Content-Type")
	if mediaType != "" {
		mediaType, _, _ = strings.Cut(mediaType, ";")
		mediaType = strings.ToLower(strings.TrimSpace(mediaType))
	}

	var offer string

	switch mediaType {
	case "application/json":
		var desc pion.SessionDescription
		if err := json.NewDecoder(r.Body).Decode(&desc); err != nil {
			log.Error().Err(err).Caller().Send()
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		offer = desc.SDP

	case "application/x-www-form-urlencoded":
		if err := r.ParseForm(); err != nil {
			log.Error().Err(err).Caller().Send()
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		offerB64 := r.Form.Get("data")
		b, err := base64.StdEncoding.DecodeString(offerB64)
		if err != nil {
			log.Error().Err(err).Caller().Send()
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		offer = string(b)

View on GitHub (pinned to c245815e75)