I am using livekit egress and aws s3 to save audio...
# ask-ai
h
I am using livekit egress and aws s3 to save audio recordings between AI agent and user. Audio file is getting saved in S3 but it is of 0 bytes even when actually call answered and conversation went on for several minutes.
✅ 1
t
Thanks for reaching out, Uddeshya. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — sorry you’re seeing 0-byte files. I’ll help diagnose this. A few quick questions and checks will narrow it down: 1) Which egress type are you using — track, track composite, or room composite? 2) Are you using LiveKit’s S3 config with access keys or an IAM role (
assume_role_arn
)? 3) Do you get
egress_started
and
egress_ended
webhook events for the recording? If yes, does
egress_ended
include
backupStorageUsed: true
or a
manifestLocation
? 4) Can you share the S3 object metadata (Content-Length shown in S3 console) and the S3 upload status (completed/failed) in your console or logs? 5) Do you have agent/egress debug logs around the session end? Look for messages about upload success/failure or “finished uploading”. While you gather that, try these checks: • Confirm egress actually produced an output file locally/backup: if backup storage was used the manifest URL (from
egress_ended
) contains the backup recording for 7 days. • Verify the egress worker wasn’t terminated before upload completed (check agent/egress logs at session end). • If using IAM role, ensure temporary credentials were valid for the upload window; expired or insufficient permissions can cause zero-byte or failed uploads. • If using
track
egress, note audio tracks are exported as OGG (Opus); ensure whatever reads the file supports OGG. Send the answers above plus any relevant webhook payloads or egress logs and I’ll give next-step actions based on what we find. Sources: Universal Egress | LiveKit | Backup Egress Recordings | Why is my Agent insights data missing? | LiveKit, with interactive elements
h
1. I am using
egressClient.startRoomCompositeEgress
to start egress session. 2. Using access keys 3. No
egress_started
and
egress_ended
events. 4. Egress events snapshot attached I noticed one thing that for this call of 35 seconds, there is only less than a second gap between EGRESS_ACTIVE and EGRESS_ENDING event
t
Thanks — this is helpful. I’m responding to your points about using
startRoomCompositeEgress
, access keys, missing egress webhooks, and the very short gap between EGRESS_ACTIVE and EGRESS_ENDING. Likely causes to investigate • The egress source is closing immediately after starting (webhook shows End reason: “Source closed”), so the recorder never receives enough media before upload begins. • Egress worker may be shutting down or losing connection before the file is finalized/uploaded. • S3 upload may be attempted but aborted/failed due to permissions or transient errors (you’d normally see egress_ended or backupStorageUsed when upload fails). • If egress never fully transitions through started→active long enough, output can be empty. Immediate checks I recommend 1. Share webhook payloads (raw JSON) for the run — especially any egress lifecycle messages you receive. 2. Check egress/agent logs around the timestamps (010330 → 010406). Look for errors like upload failures, worker restarts, encoder errors, or “Source closed” reasons with more context. 3. Confirm the Room actually had published audio tracks at the moment egress became ACTIVE (verify participant presence + published tracks in room at 010332). 4. Verify your S3 access key has full PutObject and multipart/upload permissions for the target bucket and that bucket policy doesn’t block uploads. 5. Re-run with egress debug logging enabled (if you can) and note whether
egress_started
and
egress_ended
webhook events are ever delivered; missing webhooks indicates the lifecycle may be failing before webhook dispatch. 6. If possible, enable temporary egress backup and check if
backupStorageUsed
appears in
egress_ended
for future runs. If you can, please upload: • The raw webhook JSONs you do have (egress lifecycle events). • Any egress/agent logs around the session end. • Confirmation that participants with audio were published at EGRESS_ACTIVE timestamp. With those I can point to a targeted next step (permission fix, replaying egress with debug logs, or investigating source closure reasons). , with interactive elements
h
This is the egress_ended event data I received
Copy code
WebhookEvent {
  event: 'egress_ended',
  id: 'EV_YdY83txtQvgj',
  createdAt: 1777494318n,
  numDropped: 0,
  egressInfo: EgressInfo {
    egressId: 'EG_CzH6QfUsf4nG',
    roomId: 'RM_JyTLJYkddyKv',
    roomName: 'TR-3A9VSFzlpqO6',
    sourceType: 1,
    status: 3,
    startedAt: 1777494244116605740n,
    endedAt: 1777494316139624178n,
    updatedAt: 1777494316139624178n,
    request: { case: 'roomComposite', value: [RoomCompositeEgressRequest] },
    streamResults: [],
    fileResults: [ [FileInfo] ],
    segmentResults: [],
    imageResults: [],
    error: '',
    errorCode: 0,
    details: 'End reason: Source closed',
    manifestLocation: '',
    backupStorageUsed: false,
    retryCount: 0,
    result: { case: 'file', value: [FileInfo] }
  }
}
t
Thanks — this is helpful. I’m responding to the
egress_ended
payload you shared (End reason: “Source closed”,
backupStorageUsed: false
, empty
manifestLocation
). Quick conclusions from that payload • Egress recorded a file result but reported the end reason as Source closed, and no backup was used. • That indicates the egress process stopped because the media source closed before a successful upload/finalization completed — which explains the zero-byte file symptom. Please provide these next items so I can pinpoint the cause: 1. The full
FileInfo
object from
fileResults
/
result.file
(includes path, size, and key). 2. Egress/agent logs around 010330 → 010406 (start→end). Look for upload, encoder, or permission errors. 3. Confirmation that at EGRESS_ACTIVE (010332) the room had published audio tracks (participant list + published tracks). 4. The S3 object metadata (Content-Length, ETag) and the S3 console upload status for that object. 5. Whether the same run ever produced
egress_started
or other webhook events for prior successful recordings. Immediate checks you can run now • Verify your S3 access key has PutObject and multipart upload permissions on the target bucket and that the bucket policy doesn’t block uploads. • Search egress logs for messages about “uploading” or any upload error right before the
Source closed
timestamp. • Re-run with egress debug logging (so we can see if the worker attempts an upload and why it might abort). Send the FileInfo JSON and the logs you can share and I’ll give targeted next steps (permission fix, replay steps, or escalation details). , with interactive elements
h
This is complete JSON payload received on ergess_ended event
Copy code
{
  "event": "egress_ended",
  "id": "EV_zUwG3pE7RWez",
  "createdAt": "1777496173",
  "egressInfo": {
    "egressId": "EG_Dj58eqVcGcKA",
    "roomId": "RM_2BJjo9XeqccA",
    "status": "EGRESS_COMPLETE",
    "roomComposite": {
      "roomName": "TR-Nx1eOGjwjIQP",
      "layout": "",
      "audioOnly": true,
      "videoOnly": false,
      "customBaseUrl": "",
      "file": {
        "fileType": "OGG",
        "filepath": "qa/call_recordings/TR-Nx1eOGjwjIQP.ogg",
        "s3": {
          "accessKey": "{access_key}",
          "secret": "{secret}",
          "region": "ap-south-1",
          "endpoint": "",
          "bucket": "shiprocket-livekit-data",
          "forcePathStyle": false,
          "metadata": {},
          "tagging": "",
          "contentDisposition": "",
          "sessionToken": "",
          "assumeRoleArn": "",
          "assumeRoleExternalId": ""
        },
        "disableManifest": true
      },
      "fileOutputs": [
        {
          "fileType": "OGG",
          "filepath": "qa/call_recordings/TR-Nx1eOGjwjIQP.ogg",
          "s3": {
            "accessKey": "{access_key}",
            "secret": "{secret}",
            "region": "ap-south-1",
            "endpoint": "",
            "bucket": "shiprocket-livekit-data",
            "forcePathStyle": false,
            "metadata": {},
            "tagging": "",
            "contentDisposition": "",
            "sessionToken": "",
            "assumeRoleArn": "",
            "assumeRoleExternalId": ""
          },
          "disableManifest": true
        }
      ],
      "streamOutputs": [],
      "segmentOutputs": [],
      "imageOutputs": [],
      "audioMixing": "DEFAULT_MIXING",
      "webhooks": []
    },
    "file": {
      "filename": "qa/call_recordings/TR-Nx1eOGjwjIQP.ogg",
      "startedAt": "1777496126916512745",
      "endedAt": "1777496171887820861",
      "size": "382456",
      "location": "<https://shiprocket-livekit-data.s3.amazonaws.com/qa/call_recordings/TR-Nx1eOGjwjIQP.ogg>",
      "duration": "44971308116"
    },
    "error": "",
    "startedAt": "1777496124702830272",
    "endedAt": "1777496172178965531",
    "roomName": "TR-Nx1eOGjwjIQP",
    "streamResults": [],
    "fileResults": [
      {
        "filename": "qa/call_recordings/TR-Nx1eOGjwjIQP.ogg",
        "startedAt": "1777496126916512745",
        "endedAt": "1777496171887820861",
        "size": "382456",
        "location": "<https://shiprocket-livekit-data.s3.amazonaws.com/qa/call_recordings/TR-Nx1eOGjwjIQP.ogg>",
        "duration": "44971308116"
      }
    ],
    "segmentResults": [],
    "updatedAt": "1777496172178965531",
    "imageResults": [],
    "details": "End reason: StopEgress API",
    "errorCode": 0,
    "manifestLocation": "",
    "backupStorageUsed": false,
    "sourceType": "EGRESS_SOURCE_TYPE_SDK",
    "retryCount": 0
  },
  "numDropped": 0
}
t
Thanks — I’m responding to the
egress_ended
payload you shared. What this payload shows • The egress completed with a file result:
size: "382456"
and a valid
location
URL — so this run produced a non‑empty OGG file on S3. • The
details
field shows "End reason: StopEgress API" (previous runs showed "Source closed"), and
backupStorageUsed: false
. Why you might have seen 0‑byte files earlier • If egress was stopped (or the source closed) almost immediately after becoming ACTIVE, the recorder can finalize an empty or incomplete upload. • Missing `egress_started`/`egress_ended` webhooks in earlier runs suggests the lifecycle ended too quickly or webhook delivery wasn’t configured for that run. Next targeted checks and steps 1. Confirm audio tracks are published in the Room at the moment egress becomes ACTIVE. If no audio is published, the output can be empty. 2. Don’t call StopEgress (or close the source) until the recording duration you expect has elapsed — your earlier event timeline showed a very short ACTIVE→ENDING gap. 3. Add webhook endpoints in the egress request (the payload shows
"webhooks": []
) so you reliably receive `egress_started`/`egress_ended` for future runs. 4. Verify S3 permissions for the access key: ensure PutObject and multipart upload permissions and bucket policy allow writes to the
qa/call_recordings/
path. 5. Enable debug logging for the egress/agent and look for upload messages like “uploading” / “finished uploading” or any upload errors around the run timestamps. 6. If you still see a zero‑byte object, compare the S3 object metadata (Content‑Length) vs the
fileResults.size
in the webhook. If webhook shows a non‑zero size but S3 object is zero bytes, collect egress logs showing the upload attempt. If you want, send: • The S3 object metadata (Content‑Length, ETag) for a zero‑byte object, and • Egress/agent logs around the start→end timestamps for a failing run (the exact lines showing upload attempts/errors). I’ll review those and give the exact next action. , with interactive elements