l
hi
t
Thanks for reaching out, Pavan. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Hey there! Could you share a bit more about your question or what’s going on? That way, I can help you out in the best way possible.
l
how to get error in frontend from room
t
Hey there! If you want to catch error info in the frontend from a LiveKit room, you can use some handy error callback props that the LiveKit client SDKs offer. Here are the main ways to do it: • Use the
onError
,
onMediaDeviceFailure
, and
onEncryptionError
props:
These come with the
LiveKitRoom
component. Here's a quick example:
Copy code
<LiveKitRoom 
  serverUrl="{serverUrl}" 
  token="{token}"
  onError={(error) => {
    console.error('LiveKit error occurred:', error);
  }}
  onMediaDeviceFailure={(failure) => {
    console.error('Media device failure:', failure);
    if (failure === 'PermissionDenied') {
      alert('Please grant camera/microphone permissions');
    }
  }}
  onEncryptionError={(error) => {
    console.error('Encryption error:', error);
  }}
  onDisconnected={() => {
    console.log('Disconnected from room');
  }}
/>
Keep an eye on the
error
field in `RoomState`:
If this field shows up, it tells you the current error state of the room. • Listen for specific error events: You can subscribe to events like
mediaDevicesError
,
encryptionError
, or
disconnected
to get detailed error info or reasons for disconnects. If you want a hand setting this up or have any questions, just let me know—I’m happy to help! Sources: docs/interfaces/RoomState.html | README.md | Sources/LiveKit/Errors.swift | Runtime/Scripts/Room/Errors.cs | README.md
l
"use client"; import { useFormik } from "formik"; import { Box, Button, Icon, Input, Toaster, Typography } from "@repo/ui"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { AgentSessionModalProps } from "../agent-session-modal/type"; import { agentCallSchema } from "./schema"; import { useQuery } from "../../../query"; import { getPhoneNumbersBuilder, makeTwilioCallBuilder, getConnectionDetails, GetConnectionDetailsResponse, } from "@repo/api-client"; import NiceModal from "@ebay/nice-modal-react"; import { NoPhoneNumbersModal } from "../select-phone-modal"; import { useMutation } from "../../../mutation"; import { useParams } from "next/navigation"; import { formatTime } from "../../../utils"; import { CallStatus } from "./type"; import { createLocalAudioTrack, LocalAudioTrack, Room } from "livekit-client"; import { RoomAudioRenderer, RoomContext } from "@livekit/components-react"; import { LiveTranscriptionView } from "../transcript-view"; import { forwardRef, useImperativeHandle } from "react"; import { BarVisualizer } from "../bar-visualizer"; import { useMicVolume } from "../../../(dashboard)/agent/hook/useMicVolume"; export const AgentCall = forwardRef( ({ variant = "web-call", hide }: AgentSessionModalProps, ref) => { const [callStatus, setCallStatus] = useState<CallStatus>(CallStatus.IDLE); const [callDuration, setCallDuration] = useState(0); const [isEmpty, setIsEmpty] = useState(true); const intervalRef = useRef<NodeJS.Timeout | null>(null); const searchParams = useParams(); const agentId = searchParams.id as string; const [micTrack, setMicTrack] = useState<LocalAudioTrack | null>(null); const [isMuted, setIsMuted] = useState(true); const [room] = useState(() => new Room()); const micTrackRef = useRef<MediaStreamTrack | null>(null); const volume = useMicVolume(micTrack); const [micDevices, setMicDevices] = useState<{ label: string; value: string }[]>([]); useImperativeHandle(ref, () => ({ endCall: handleEndCall, })); const { data: numbers = [] } = useQuery({ ...getPhoneNumbersBuilder, resolver() { return getPhoneNumbersBuilder.resolver("1"); }, options: { enabled: variant === "twilio-call", }, }); const selectedNumber = useMemo(() => { return numbers.find(n => n.agentId === agentId); }, [numbers, agentId]); const { data: phoneNumbers } = useQuery({ ...getPhoneNumbersBuilder, resolver() { return getPhoneNumbersBuilder.resolver("1"); }, options: { enabled: variant === "twilio-call" && !selectedNumber, }, }); useEffect(() => { if (!intervalRef.current && !isEmpty) { intervalRef.current = setInterval(() => { setCallDuration(d => d + 1); }, 1000); setIsMuted(false); micTrack.unmute(); } return () => { if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } }; }, [isEmpty]); const cleanupCall = () => { room.disconnect().catch(() => {}); if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } if (micTrack) { micTrack.stop(); setMicTrack(null); setIsMuted(false); } }; const handleEndCall = async () => { try { await room.disconnect(); if (micTrack) { micTrack.stop(); micTrackRef.current = null; } } catch (error) { console.error("LiveKit disconnect error:", error); } cleanupCall(); setCallStatus(CallStatus.DISCONNECTED); }; const { mutate: getLivekitToken, isPending } = useMutation(getConnectionDetails); const startCall = useCallback(async (_data_: GetConnectionDetailsResponse) => { if (room.state !== "connected") { await room.connect(data.serverUrl, data.participantToken, { autoSubscribe: true, }); } await fetchMicDevices(); await room.localParticipant.setName(data.participantName); try { const defaultDeviceId = micDevices[0]?.value; const micTrack = await createLocalAudioTrack({ deviceId: defaultDeviceId }); micTrackRef.current = micTrack.mediaStreamTrack; await room.localParticipant.publishTrack(micTrack); setMicTrack(micTrack); micTrack.mute(); setIsMuted(true); } catch (error) { console.error("Error accessing microphone:", error); Toaster({ variant: "error", message: "Microphone access denied." }); return; } setCallStatus(CallStatus.CONNECTED); setCallDuration(0); }, []); const startLiveKitCall = async () => { try { getLivekitToken( { agent: agentId, user: "user_" + Math.floor(Math.random() * 10000), room: "room_" + Math.floor(Math.random() * 10000), identity: "identity_" + Math.floor(Math.random() * 10000), }, { onSuccess: async data => { await startCall(data); }, onError: err => { console.error("LiveKit Token Error:", err); Toaster({ variant: "error", message: "Unable to start call." }); }, } ); } catch (err) { console.error("LiveKit call start error:", err); Toaster({ variant: "error", message: "Call failed to start." }); } }; const { mutate: twilioCall, isPending: isLoading } = useMutation(makeTwilioCallBuilder); const { values, handleSubmit, handleChange, touched, errors, handleBlur, isValid, setFieldValue, } = useFormik({ initialValues: { phoneNumber: "", name: "", agentPhoneNumber: selectedNumber?.phoneNumber || "", }, enableReinitialize: true, validationSchema: variant === "twilio-call" ? agentCallSchema : undefined, onSubmit: async values => { if (variant === "web-call") { await startLiveKitCall(); return; } twilioCall( { agent_name: agentId, participant_name: values.name, phone_number:
+91${_values_.phoneNumber}
, }, { onSuccess: () => { Toaster({ variant: "success", message: "Agent is calling. Please pick up call.", }); hide(); }, onError: error => { console.error("Call error:", error); Toaster({ variant: "error", message: "Failed to initiate call. Please try again.", }); }, } ); }, }); const handleShowSelectPhoneNumber = () => { NiceModal.show(NoPhoneNumbersModal, { numbers: phoneNumbers || [], setSelectedNumber: (_number_: string) => { setFieldValue("agentPhoneNumber", number); }, }); }; useEffect(() => { const onParticipantConnected = () => { if (!intervalRef.current) { intervalRef.current = setInterval(() => { setCallDuration(d => d + 1); }, 1000); } }; const onDisconnected = () => { setCallStatus(CallStatus.DISCONNECTED); if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } }; room.on("participantConnected", onParticipantConnected); room.on("disconnected", onDisconnected); return () => { room.off("participantConnected", onParticipantConnected); room.off("disconnected", onDisconnected); }; }, [room]); const toggleMute = async () => { if (!micTrack || room.state !== "connected") return; try { if (isMuted) { await micTrack.unmute(); setIsMuted(false); } else { await micTrack.mute(); setIsMuted(true); } } catch (err) { console.error("Error toggling mute:", err); } }; useEffect(() => { const handleKeyDown = async (_e_: KeyboardEvent) => { if (e.code === "Space" && micTrack && isMuted) { await micTrack.unmute(); setIsMuted(false); } }; const handleKeyUp = async (_e_: KeyboardEvent) => { if (e.code === "Space" && micTrack && !isMuted) { await micTrack.mute(); setIsMuted(true); } }; window.addEventListener("keydown", handleKeyDown); window.addEventListener("keyup", handleKeyUp); return () => { window.removeEventListener("keydown", handleKeyDown); window.removeEventListener("keyup", handleKeyUp); }; }, [micTrack, isMuted]); async function *fetchMicDevices*() { try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); const devices = await navigator.mediaDevices.enumerateDevices(); const audioInputs = devices .filter(d => d.kind === "audioinput") .map(d => ({ label: d.label || "Microphone", value: d.deviceId, })); setMicDevices(audioInputs); stream.getTracks().forEach(track => track.stop()); } catch (err) { console.error("Failed to get mic devices:", err); Toaster({ variant: "error", message: "Please allow microphone permission." }); } } study this code and provide soluthin
t
Thanks so much for sharing your code! Could you tell me a bit more about what’s going on? Are you running into an error, or is there a specific part of the code you’d like a hand with? I’m here to help!
l
i want error fromroom when aget is not connecting
t
When a LiveKit room doesn't connect as expected, don't worry! Here's a friendly guide to help you handle those errors smoothly: 1. Wrap the
room.connect()
call in a try-catch block:
This way, you can catch any connection hiccups right away and handle them gracefully. For example:
Copy code
try {
  await room.connect(serverUrl, token);
} catch (error) {
  console.error("Failed to connect to room:", error);
  // Show a friendly error message to your user
}
1. Tap into SDK error events: Listen for events like
onError
,
onMediaDeviceFailure
, and
onDisconnected
to catch specific issues as they happen. Here's how:
Copy code
<LiveKitRoom 
  onError={(error) => {
    console.error("Connection error:", error);
  }}
  onDisconnected={() => {
    console.log("Disconnected from room");
  }}
/>
1. Keep an eye on the room state: Check
room.connectionState
to know what's going on with the connection and update your UI to keep users in the loop. 2. Run connection diagnostics: Use the
ConnectionCheck
tool to test things like WebRTC, WebSocket, and TURN server connectivity. Here's a quick example:
Copy code
import { ConnectionCheck } from '@livekit/client-sdk';

const checker = new ConnectionCheck({ url, token });
const webrtcResult = await checker.checkWebRTC();
const turnResult = await checker.checkTURN();
1. Keep your users informed: Show clear, friendly error messages—maybe with toast alerts or modals—and give them easy options to try reconnecting. Following these steps will help you handle connection issues smoothly and make the experience better for everyone. If you want to chat more or need a hand, just let me know! Sources: Web and mobile frontends | LiveKit Docs | Diagnosing Connection Errors with Connection Test Utility | Web Embed Example | livekit/livekit_composite | DeepWiki | src/room/errors.ts | Swift quickstart | LiveKit Docs