Troubleshooting recordings#
Introduction#
This is a guide to help identify and solve common issues related to recordings in OpenVidu.
Recordings are handled by the Egress service in OpenVidu, built on top of LiveKit Egress . There are some general considerations to take into account:
- Recordings are CPU intensive operations. For recordings to work smoothly, the nodes hosting the Egress service must have sufficient free cores.
- The first symptom of something going wrong with recordings is usually missing recordings: you are expecting recording files to be available in your storage, but they are simply not there. Other common symptom is receiving an error
503 Service Unavailablewhen starting a new recording on-demand. - To be proactive when dealing with recording issues, it is highly recommended to enable webhooks in your OpenVidu deployment (learn how) and monitor it from your application's backend. In this way you can be notified of Egress-related events and errors as they happen.
On-demand vs Automatic recordings#
Recordings can be started on-demand or automatically:
- On-demand recordings are started by calling any of the these operations of the LiveKit Egress API :
StartRoomCompositeEgressStartTrackCompositeEgressStartParticipantEgressStartTrackEgressStartWebEgress
- Automatic recordings are started when a room is created with auto-egress enabled. To do so just include an
egressfield when calling theCreateRoommethod: then the room will be automatically recorded during its lifetime. See auto-egress documentation for more details.
Warning
It is important to distinguish between on-demand recordings and automatic recordings for a simple reason: your application is able to catch and handle errors when starting on-demand recordings, while automatic recordings will fail silently. In that case, enabling webhooks (or inspecting the service logs) is the only way to be notified of these failures.
List of possible recording issues#
Below are some of the most common issues related to recordings in OpenVidu, along with their symptoms and possible solutions.
CPU exhausted#
Description
There are not enough free CPU cores to start a new egress.
Symptoms
- The operation to start a new egress returns a
503 Service Unavailableerror (text of the error:no response from servers). This applies when starting egress on-demand from your application. -
Log lines of level
WARNdisplayingcan not accept requestin service "egress". A typical log line of this type looks like this:2026-03-10T10:47:56.215Z WARN egress stats/monitor.go:204 can not accept request {"nodeID": "NE_ynSpFdP6xfAS", "clusterID": "", "total": 16, "available": 3.175, "pending": 0, "used": 6.258, "activeRequests": 4, "activeWeb": 4, "memory": 2.9882, "memorySource": "proc_rss", "required": 4, "canAccept": false, "reason": "cpu", "error": "not enough CPU"} -
Log lines displaying
CPU exhaustedin service "egress". - Log lines displaying
pipeline frozenin service "egress". - Log lines displaying
Can't record audio fast enoughin service "egress".
Solutions
- Scale out your OpenVidu deployment: deploy it in nodes with more CPU cores or add more Media Nodes to your cluster.
- Consider using a less CPU intensive egress type: Track Egress does not require transcoding and composition, so it is much less CPU intensive than other egress types such as Room Composite Egress .
-
Review your
egress.yamlconfiguration file, specifically the properties withincpu_cost:cpu_cost: max_cpu_utilization: 0.8 room_composite_cpu_cost: 2.0 audio_room_composite_cpu_cost: 1.0 web_cpu_cost: 2.0 audio_web_cpu_cost: 0.5 participant_cpu_cost: 1.0 track_composite_cpu_cost: 1.0 track_cpu_cost: 0.5These properties determine two different things. OpenVidu configures sane defaults in all of them to prevent CPU exhaustion in most scenarios, but you may want to review and adjust them according to your specific use case:
- The
max_cpu_utilizationproperty is the CPU usage threshold of the node above which the egress service won't accept new requests. - All the
[...]_cpu_costproperties determine the amount of free CPU cores required in the node to accept a new egress request of each type.
- The
We strongly recommend reading the Load balancing of egress documentation.
Wrong auto-egress configuration#
Description
By default, OpenVidu will automatically create a new room when a user tries to connect to a room that does not exist yet. This behavior can be disabled in livekit.yaml configuration file:
When a room is auto-created on participant join, OpenVidu uses the room settings included in that participant’s token (RoomConfiguration ).
A common problem appears when these chain of events happens:
- Your application's backend explicitly creates a room with a specific
RoomConfiguration. For example, with anegressfield to enable auto-egress. - The room is unexpectedly deleted because of a timeout (properties
departure_timeoutorempty_timeoutinlivekit.yaml). - A participant tries to join the room using a token that does not include the same
egressfield in itsRoomConfiguration. - OpenVidu auto-creates the room again, but now using the token’s
RoomConfiguration, not the original one.
In this case, the room will not have auto-egress enabled, and therefore it will not be recorded. This behavior is also silent, as it is not an actual error but just a logic issue in your application.
Symptoms
You encounter this set of logs related to a room (in this example, room DailyMeeting):
-
You explicitly create the room from your application's backend with autoegress enabled:
2026-01-19T09:00:03.302Z INFO openvidu.api service/twirp.go:128 API RoomService.CreateRoom {"service": "RoomService", "method": "CreateRoom", "room": "DailyMeeting", "request": {"name": "DailyMeeting", "metadata": "<redacted (0 bytes)>", "egress": {"tracks": {"filepath": "recordings/{room_name}/{track_source}-{time}"}}}, "duration": "8.808101ms", "status": "200"} -
The room is automatically deleted due to a timeout:
-
A participant connects to the room with a token that does not have autoegress enabled (no
egressfield in itsRoomConfig):2026-01-19T09:00:02.925Z INFO openvidu service/roommanager.go:432 starting RTC session {"room": "DailyMeeting", "roomID": "RM_u8NSwnuczia6", "participant": "Alice", "pID": "PA_CMwzkoL6Vp3q", "remote": false, "nodeID": "ND_QLa7LB7937db", "numParticipants": 1, "participantInit": {"Identity": "1589577", "Reconnect": false, "ReconnectReason": "RR_UNKNOWN", "AutoSubscribe": false, "Client": {...}, "Grants": {...}, "SIP": {...}, "Agent": {...}, "RoomConfig": {"metadata": "<redacted (0 bytes)>"}, "RoomPreset": ""}, "Region": "", "AdaptiveStream": true, "ID": "", "SubscriberAllowPause": "not-set", "DisableICELite": false, "CreateRoom": {"name": "DailyMeeting", "metadata": "<redacted (0 bytes)>"}}
Solutions
- Consider using on-demand egress requests instead of auto egress to record rooms.
-
Consider disabling auto-creation of rooms in file
livekit.yaml. This enforces rooms to always be created explicitly from your backend before a participant can join to it with a token. Of course, it also requires handling connection errors in the frontend if a participant tries to join a non-existing room. -
If you want both auto-egress and auto-creation of rooms, make sure to include the same
egressfield in theRoomConfigurationin both theCreateRoomrequest and the participant's access token . In this way rooms will always behave the same way, no matter if they are explicitly created from your backend or auto-created when a participant tries to join. -
Increase the room timeout properties in
livekit.yaml. This can reduce the probability of rooms being cleaned up before a participant tries to join:
Misconfiguration of external storage#
Description
You have configured an external storage for your recordings, but the recording files are not being uploaded to it. This can be caused by a misconfiguration or a connectivity issue between OpenVidu nodes and the external storage.
External storage is configured in the egress.yaml configuration file globally (with storage property):
You can also configure external storage programmatically on a per-request basis when starting an egress.
Symptoms
-
You don't see recording files in your external storage, and the backup folder for egress in your OpenVidu node (
/opt/openvidu/data/egress_data/home/egress/backup_storage) is accumulating files:root@media-node-1:/opt/openvidu/data/egress_data/home/egress/backup_storage# ls -la total 19036 drwxr-xr-x 2 admin admin 4096 Mar 10 14:16 . drwxr-xr-x 8 admin admin 4096 Mar 10 10:47 .. -rw-r--r-- 1 admin root 335 Mar 10 14:11 EG_53MKZu5havzx.json -rw-r--r-- 1 admin root 335 Mar 10 14:09 EG_ZgeLTs8FdtzU.json -rw-r--r-- 1 admin root 448 Mar 10 14:16 EG_fnvyJkL8YVyt.json -rw-r--r-- 1 admin root 14721461 Mar 10 14:09 RoomComposite-RM_Mi5Lau9HWGQ8-TestRoom-2026-03-10T140854.mp4 -rw-r--r-- 1 admin root 1772593 Mar 10 14:11 RoomComposite-RM_Mi5Lau9HWGQ8-TestRoom2-2026-03-10T141055.mp4 -rw-r--r-- 1 admin root 2969013 Mar 10 14:16 TrackComposite-RM_JpcnziWBTEdY-TestRoom3-2026-03-10T141625.mp4 -
The EgressInfo objects returned by the Egress API or included in the
egress_endedwebhook event contains:- Field
backup_storage_usedwith valuetrue. - Or any other field with the substring
backup_storagein its value (E.g.manifestLocation = '/home/egress/backup_storage/EG_f5nHLam4xLb8.json').
- Field
Solutions
- Review the configuration of the external storage: in the
egress.yamlif you are using the global declarative configuration, or in the body of theCreateRoomorStartEgressoperations if you are using the programmatic per-request configuration. - Review the connectivity between your OpenVidu nodes and the external storage. Check if there are any network issues or firewall rules preventing OpenVidu nodes from reaching the external storage.
No disk space free#
Description
There is not enough free disk space in the node hosting the egress to start a new recording.
Symptoms
- The operation to start a new egress returns a
503 Service Unavailableerror (text of the error:no response from servers). This applies when starting egress on-demand from your application. -
The egress service logs a warning message
can not accept requestfollowed by a JSON object containing"error": "not enough disk space":{"nodeID": "NE_xRcu2tzJD5mC", "clusterID": "", "total": 16, "available": 12.022278481012659, "pending": 0, "used": 0, "activeRequests": 0, "activeWeb": 0, "memory": 0.06648635864257812, "memorySource": "proc_rss", "availableDiskMB": 30704.07421875, "minDiskSpaceMB": 99999999, "canAccept": false, "reason": "disk", "error": "not enough disk space"} -
Webhook event
egress_endedwith statusEGRESS_FAILEDand errormkdir /home/egress/tmp/EGH_XXXXXXXXXXXX: no space left on device{"event": "egress_ended", "id": "EV_zxMEf5XPp5yV", "webhookTime": 1771329474, "url": "http://127.0.0.1:6080/livekit/webhook", "egressID": "", "status": "EGRESS_FAILED", "error": "mkdir /home/egress/tmp/EGH_XXXXXXXXXXXX: no space left on device", "queueDuration": "7.815288ms", "qLen": 0, "sendDuration": "8.05349ms"}
Solutions
Increase disk space in the node hosting the egress.
Egress CPU overload killer#
Description
By default, OpenVidu will kill active egresses under sustained high CPU load (see Egress CPU overload killer). This is generally expected and desired, but it can impact active egresses, causing them to be interrupted.
Symptoms
- The recordings are incomplete. They appear to be cut off unexpectedly.
- Log lines of level
WARNdisplayingkilling egress due to sustained high cpuin service "egress".
Solutions
- You can disable the Egress CPU overload killer by setting property
openvidu.disable_cpu_overload_killertotruein theegress.yamlconfiguration file. This must be done with caution and care, as it could lead to overloading the entire Media Node and affecting the performance of other processes. - In the end this is just an under-provisioning issue: consider scaling out your OpenVidu deployment. Deploy it in nodes with more CPU cores or add more Media Nodes to your cluster.
Track disappears before the recorder attaches (track not found)#
Description
When recording individual tracks with Track Egress (on-demand via StartTrackEgress, or with per-track auto-egress), OpenVidu launches one egress per published track, shortly after the track is published. The recorder then needs a moment to connect to the room and subscribe to the track: usually well under a second, occasionally a little longer. If the track is unpublished during that window, the recorder never finds it, and the egress fails after waiting up to 30 seconds.
The most common trigger is participant reconnection: when a client reconnects, its camera and microphone are republished with new track IDs, so the egress that was launched for the old track ID is orphaned. Other triggers are very short-lived publishes and quick unpublishes. This is a startup race tied to the track lifecycle, not a capacity problem.
In almost all cases no established recording is lost: the participant's stream keeps being recorded under the new track ID, with at most a small gap at the instant of the reconnection.
Symptoms
-
Log lines displaying
egress_failedin service "egress", with anerrorof the formtrack TR_... not found, acodeof404anddetailsEnd reason: Failure: -
Webhook event
egress_endedwith statusEGRESS_FAILEDand the sametrack ... not founderror: -
The failure occurs around 30 seconds after the egress was requested (the recorder's wait timeout).
- The pattern is associated with participants that reconnect frequently.
To confirm it is a track-lifecycle race, reconstruct the track's lifecycle across the "openvidu" and "egress" services. A textbook sequence (the track is published and unpublished less than half a second later, before the recorder could attach):
10:00:00.100 openvidu mediaTrack published trackID TR_a1b2C3d4E5f6g7 (audio, MICROPHONE)
10:00:00.540 openvidu track_unpublished TR_a1b2C3d4E5f6g7 (the track lived ~0.44 s)
10:00:00.600 egress request validated egressID EG_x7y8Z9w0V1u2, track_id TR_a1b2C3d4E5f6g7
10:00:30.600 egress egress_failed "track TR_a1b2C3d4E5f6g7 not found" (+30 s)
Then look for a new mediaTrack published from the same participant identity in the same room shortly afterwards: that is the reconnected track, which continues to be recorded under its new ID.
Solutions
- In most deployments this is expected and benign; no action is needed beyond confirming that the participant's stream is captured under another track ID.
- It is not caused by egress capacity limits, a specific node, or storage.
The recorder attaches but no media arrives (Source closed)#
Description
The recorder connects and subscribes to the track successfully, but the recording pipeline never starts because no media ever arrives from the publisher. When the track finally closes, the egress aborts having recorded nothing.
This is almost always a camera track that is published without sending video: a participant who joins with the camera off or muted (a muted track stays published but sends no media), or whose camera has not started producing frames yet. It can also be a reconnection orphan: the track belongs to a peer connection that is being replaced, so media never stabilizes before the new connection takes over.
It is not a network, egress-node or capacity problem. The same peer connection's other tracks (for example the microphone) typically record fine at the same time, which proves the media path works; there is simply no video being sent.
Symptoms
-
Log lines displaying
egress_abortedin service "egress", witherrorStop called before pipeline could start, acodeof412anddetailsEnd reason: Source closed: -
Webhook event
egress_endedwith statusEGRESS_ABORTED: -
In the egress logs the recording reaches
pipeline paused(and the "openvidu" service logsstarting with dummy forwardingfor the egress subscriber), but it never reachesstarting track synchronizer,pipeline playingoregress_active, the markers that the first media packet arrived. A healthy recording reaches all of them within a fraction of a second of subscribing. - It predominantly affects video/camera tracks.
To confirm the camera simply was not sending video, check whether the same participant's audio track recorded successfully during the same session. It usually did, on the same connection.
Solutions
- Benign in most cases: a camera published while off has nothing to record, and reconnection orphans are recorded under the replacement track.
- If recordings you expect are genuinely missing, verify on the client side that video tracks are only published when the camera is actually active (or handle mute / camera-off state appropriately), and reduce client reconnections.
- As with
track not found, it is not an egress capacity or node issue.