{"record":{"id":"a145d6dc96fdef52","repo":"AlexxIT/go2rtc","slug":"hap-no-free-streams","errorCode":null,"errorMessage":"hap: no free streams","messagePattern":"hap: no free streams","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/hap/camera/stream.go","lineNumber":102,"sourceCode":"\t}\n\n\tfor _, srv := range acc.Services {\n\t\tfor _, char := range srv.Characters {\n\t\t\tif char.Type == TypeStreamingStatus {\n\t\t\t\tvar status StreamingStatus\n\t\t\t\tif err = char.ReadTLV8(&status); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\tif status.Status == StreamingStatusAvailable {\n\t\t\t\t\ts.service = srv\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn errors.New(\"hap: no free streams\")\n}\n\nfunc (s *Stream) ExchangeEndpoints(videoSession, audioSession *srtp.Session) error {\n\treq := SetupEndpointsRequest{\n\t\tSessionID: s.id,\n\t\tAddress: Address{\n\t\t\tIPVersion:    0,\n\t\t\tIPAddr:       videoSession.Local.Addr,\n\t\t\tVideoRTPPort: videoSession.Local.Port,\n\t\t\tAudioRTPPort: audioSession.Local.Port,\n\t\t},\n\t\tVideoCrypto: SRTPCryptoSuite{\n\t\t\tMasterKey:  string(videoSession.Local.MasterKey),\n\t\t\tMasterSalt: string(videoSession.Local.MasterSalt),\n\t\t},\n\t\tAudioCrypto: SRTPCryptoSuite{\n\t\t\tMasterKey:  string(audioSession.Local.MasterKey),\n\t\t\tMasterSalt: string(audioSession.Local.MasterSalt),","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/AlexxIT/go2rtc/blob/c245815e75e2a5fd60b4290f12bfc04e55a984d3/pkg/hap/camera/stream.go#L84-L120","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: unbounded concurrent viewers exhaust the pool\nfor _, viewer := range viewers { go viewer.Start(stream) }\n// after: serialize with a semaphore of capacity = stream count\nsem := make(chan struct{}, cameraStreamCount)\nfor _, viewer := range viewers {\n    go func(v *Viewer) {\n        sem <- struct{}{}\n        defer func() { <-sem }()\n        v.Start(stream)\n    }(viewer)\n}","handlingStrategy":"retry","validationCode":"// cap concurrency at the number of camera streams before requesting one\nconst maxStreams = 2\nsem := make(chan struct{}, maxStreams)\nsem <- struct{}{} // blocks until a slot (stream) is available\ndefer func() { <-sem }()","typeGuard":"func hasFreeStream(count, active int) bool { return active < count }","tryCatchPattern":"stream, err := cameraStream.GetFreeStream()\nif err != nil {\n    if strings.Contains(err.Error(), \"no free streams\") {\n        time.Sleep(2 * time.Second) // wait for a session to release\n        stream, err = cameraStream.GetFreeStream()\n    }\n    if err != nil { return err }\n}","preventionTips":["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."],"tags":["hap","stream","resource-exhaustion","homekit"],"backgroundTag":"resource-exhausted","analyzedSha":"c245815e75e2a5fd60b4290f12bfc04e55a984d3","analyzedAt":"2026-09-07T11:47:02.965Z","contentChangedAt":"2026-09-07T11:47:02.965Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}