AlexxIT/go2rtc · error

start from CONN state

Error message

start from CONN state

What it means

Conn.Start() drives the state machine and refuses to start while the connection is stuck in StateConn (connected/authed but no media has been SET UP). Start expects either StateNone (nothing to do) or StateSetup (tracks prepared, ready to PLAY). Reaching StateConn means tracks were never added via GetTrack/SetupMedia, so there is nothing to play and Start is called at the wrong point in the lifecycle.

Solutions

  1. Ensure GetTrack(media, codec) is called for at least one track before Start(), so the state moves from StateConn to StateSetup.
  2. Check the DESCRIBE SDP: if no codec matched your consumer, GetTrack was never invoked — add or negotiate a supported codec.
  3. Verify call order: Dial -> Options/Describe -> GetTrack(s) -> Start.
  4. Inspect for concurrent goroutines racing on the Conn and resetting c.state.

Example fix

// before
conn, _ := rtsp.Dial(url)
conn.Options()
conn.Describe()
err := conn.Start() // state == StateConn -> error
// after
conn, _ := rtsp.Dial(url)
conn.Options()
conn.Describe()
for _, media := range conn.Medias {
    if _, err := conn.GetTrack(media, media.Codecs[0]); err != nil {
        return err
    }
}
err := conn.Start() // state == StateSetup -> PLAY
Defensive patterns

Strategy: validation

Validate before calling

if conn.State() == StateConn {
    return errors.New("call GetTrack before Start: no tracks set up")
}

Try / catch

if err := conn.Start(); err != nil {
    if strings.Contains(err.Error(), "start from CONN state") {
        // recover: run track setup (GetTrack) then retry Start
    }
}

Prevention

When it happens

Trigger: Calling Start() after Dial/Options/Describe succeeded but before any GetTrack(media, codec) call put the Conn into StateSetup; the state machine hits case StateConn and returns this error.

Common situations: Application error handling skipped the GetTrack step (e.g. no matching codecs in DESCRIBE so GetTrack was never called); calling Start immediately after dialing in custom code; race where another goroutine reset the state; producer code paths that skipped track setup because SDP offered no supported codecs.

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

Appendix: source

Thrown at pkg/rtsp/producer.go:64

	track := core.NewReceiver(media, codec)
	track.ID = channel
	c.Receivers = append(c.Receivers, track)

	return track, nil
}

func (c *Conn) Start() (err error) {
	core.Assert(c.mode == core.ModeActiveProducer || c.mode == core.ModePassiveProducer)

	for {
		ok := false

		c.stateMu.Lock()
		switch c.state {
		case StateNone:
			err = nil
		case StateConn:
			err = errors.New("start from CONN state")
		case StateSetup:
			switch c.mode {
			case core.ModeActiveProducer:
				err = c.Play()
			case core.ModePassiveProducer:
				err = nil
			default:
				err = errors.New("start from wrong mode: " + c.mode.String())
			}

			if err == nil {
				c.state = StatePlay
				ok = true
			}
		}
		c.stateMu.Unlock()

		if !ok {

View on GitHub (pinned to c245815e75)