AlexxIT/go2rtc · warning
hap: no free streams
Error message
hap: no free streams
What it means
GetFreeStream scans the HAP camera's fixed pool of streams and returns this error when every stream is already occupied by an active HomeKit session. The library does not preempt existing sessions; the caller must wait for one to be released or increase the stream count.
Solutions
- Ensure all streams are properly closed when sessions end (check for leaked sessions from crashed clients).
- Reduce concurrent viewers or serialize stream access behind a mutex/semaphore in your code.
- Restart the camera accessory process to clear leaked stream reservations.
- Increase the configured stream count for the HAP camera accessory if the device supports it.
Example fix
// before: unbounded concurrent viewers exhaust the pool
for _, viewer := range viewers { go viewer.Start(stream) }
// after: serialize with a semaphore of capacity = stream count
sem := make(chan struct{}, cameraStreamCount)
for _, viewer := range viewers {
go func(v *Viewer) {
sem <- struct{}{}
defer func() { <-sem }()
v.Start(stream)
}(viewer)
} Defensive patterns
Strategy: retry
Validate before calling
// cap concurrency at the number of camera streams before requesting one
const maxStreams = 2
sem := make(chan struct{}, maxStreams)
sem <- struct{}{} // blocks until a slot (stream) is available
defer func() { <-sem }() Type guard
func hasFreeStream(count, active int) bool { return active < count } Try / catch
stream, err := cameraStream.GetFreeStream()
if err != nil {
if strings.Contains(err.Error(), "no free streams") {
time.Sleep(2 * time.Second) // wait for a session to release
stream, err = cameraStream.GetFreeStream()
}
if err != nil { return err }
} Prevention
- Close every stream/session deterministically (defer Close) to avoid leaks.
- Limit concurrent HomeKit viewers to the configured stream count.
- Monitor for sessions that never release; restart the accessory process if the pool stays exhausted.
When it happens
Trigger: More concurrent HomeKit viewers than configured streams (e.g. 2 viewers on a 1-stream camera); a stale session that never released its stream; NewStream called in a loop without closing previous streams.
Common situations: Multiple HomeKit hubs/clients (iPhone + iPad + HomeHub) watching simultaneously; a crashed viewer whose session was not torn down, leaking the stream; testing scripts that open streams without closing them.
Related errors
- hap: GetAccessories zero answer
- hap: wrong http status:
- hap: PairVerify with unknown client_id:
- hap: wrong request: %#v
- hap: wrong response: %#v, on request: %#v
AI-assisted analysis of AlexxIT/go2rtc@c245815e75 (2026-09-07).
Data as JSON: /api/errors/a145d6dc96fdef52.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/hap/camera/stream.go:102
}
for _, srv := range acc.Services {
for _, char := range srv.Characters {
if char.Type == TypeStreamingStatus {
var status StreamingStatus
if err = char.ReadTLV8(&status); err != nil {
return err
}
if status.Status == StreamingStatusAvailable {
s.service = srv
return nil
}
}
}
}
return errors.New("hap: no free streams")
}
func (s *Stream) ExchangeEndpoints(videoSession, audioSession *srtp.Session) error {
req := SetupEndpointsRequest{
SessionID: s.id,
Address: Address{
IPVersion: 0,
IPAddr: videoSession.Local.Addr,
VideoRTPPort: videoSession.Local.Port,
AudioRTPPort: audioSession.Local.Port,
},
VideoCrypto: SRTPCryptoSuite{
MasterKey: string(videoSession.Local.MasterKey),
MasterSalt: string(videoSession.Local.MasterSalt),
},
AudioCrypto: SRTPCryptoSuite{
MasterKey: string(audioSession.Local.MasterKey),
MasterSalt: string(audioSession.Local.MasterSalt),View on GitHub (pinned to c245815e75)