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
- Ensure GetTrack(media, codec) is called for at least one track before Start(), so the state moves from StateConn to StateSetup.
- Check the DESCRIBE SDP: if no codec matched your consumer, GetTrack was never invoked — add or negotiate a supported codec.
- Verify call order: Dial -> Options/Describe -> GetTrack(s) -> Start.
- 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
- Always follow the lifecycle: Dial -> Describe -> GetTrack(s) -> Start.
- Guard Start behind a helper that performs track setup if state is StateConn.
- Log c.state before Start in debug builds to catch ordering bugs early.
- Handle the case where DESCRIBE yields no supported codecs — skip Start instead of calling it trackless.
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
- start from wrong mode
- nest: tried to stop rtsp stream without a project or device…
- rtsp: wrong mode for GetTrack
- exec: rtsp module disabled
- exec: timeout
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)