TestAgent.tsx:149 Failed to connect: ConnectionErr...
# ask-ai
b
TestAgent.tsx:149 Failed to connect: ConnectionError: could not establish pc connection at _ConnectionError.internal (livekit-client.js?v=ec1baf64994312) at livekit-client.js?v=ec1baf641503336 how can i solve this i want quick solution
t
Thanks for reaching out, Vishnu. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I see the error is "could not establish pc connection" and you want a quick fix. A few fast checks will usually resolve this; please try them and tell me which step fails. 1. Run the LiveKit Connection Tester with your server URL and token: https://www.livekit.io/connection-test. Tell me whether WebSocket, WebRTC, or TURN fails. 2. If WebRTC or TURN fails, disable any VPN/firewall temporarily (or test from a different network). Corporate networks often block UDP. 3. If UDP is blocked, force TURN relay in your client:
Copy code
await room.connect(url, token, {
  rtcConfig: { iceTransportPolicy: 'relay' },
});
1. Verify browser/device permissions (camera/mic) and try an incognito window or a different browser. 2. Confirm your LiveKit server is reachable and TLS is correct (WSS/HTTPS). If self-hosted, check server logs. Which step did you try and what was the Connection Tester result? If you share the tester output or your rtcConfig and whether you’re on a corporate network, I’ll give the exact next step. Sources: Diagnosing Connection Errors with Connection Test Utility | Firewall Tips | Running LiveKit on AWS
b
import { DashboardLayout } from "@/components/layout/DashboardLayout"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Badge } from "@/components/ui/badge"; import { MessageSquare, PhoneOff, Loader2, AlertCircle } from "lucide-react"; import { useState, useEffect, useRef } from "react"; import { apiClient } from "@/services/api"; import { ChatInterface } from "@/components/agent/ChatInterface"; import { BidirectionalTranscription } from "@/components/agent/BidirectionalTranscription"; import { Room, RoomEvent, Track, RemoteTrack, RemoteTrackPublication, RemoteParticipant, RoomOptions, VideoPresets } from "livekit-client"; import { toast as sonnerToast } from "sonner"; const DEFAULT_AGENT_ID = import.meta.env.VITE_DEFAULT_AGENT_ID || '00000000-0000-0000-0000-000000000001'; export default function TestAgent() { // LiveKit connection state const [room, setRoom] = useState<Room | null>(null); const [isConnecting, setIsConnecting] = useState(false); const [isConnected, setIsConnected] = useState(false); const [roomName, setRoomName] = useState<string>(""); const audioContainerRef = useRef<HTMLDivElement>(null); // Cleanup on unmount useEffect(() => { return () => { if (room) { room.disconnect(); } }; }, [room]); const connectToAgent = async () => { try { setIsConnecting(true); // Get access token from backend using default agent const response = await apiClient.createPreviewToken(DEFAULT_AGENT_ID); setRoomName(response.room_name); // Create LiveKit room with options const roomOptions: RoomOptions = { adaptiveStream: true, dynacast: true, videoCaptureDefaults: { resolution: VideoPresets.h720.resolution, }, }; const newRoom = new Room(roomOptions); // Set up event listeners newRoom.on(RoomEvent.Connected, () => { console.log("Connected to room"); setIsConnected(true); setIsConnecting(false); sonnerToast.success("Connected to agent!"); }); newRoom.on(RoomEvent.Disconnected, (reason) => { console.log("Disconnected from room:", reason); setIsConnected(false); setRoom(null); if (reason) { sonnerToast.info(
Disconnected: ${reason}
); } else { sonnerToast.info("Disconnected"); } }); newRoom.on(RoomEvent.Reconnecting, () => { console.log("Reconnecting to room..."); sonnerToast.info("Reconnecting..."); }); newRoom.on(RoomEvent.Reconnected, () => { console.log("Reconnected to room"); sonnerToast.success("Reconnected!"); }); newRoom.on(RoomEvent.TrackSubscribed, ( track: RemoteTrack, _publication: RemoteTrackPublication, participant: RemoteParticipant ) => { console.log("Track subscribed:", track.kind, "from", participant.identity); if (track.kind === Track.Kind.Audio) { // Attach audio track to DOM const audioElement = track.attach(); audioElement.autoplay = true; if (audioContainerRef.current) { audioContainerRef.current.appendChild(audioElement); } sonnerToast.success("Agent audio connected"); } }); newRoom.on(RoomEvent.TrackUnsubscribed, ( track: RemoteTrack, _publication: RemoteTrackPublication, participant: RemoteParticipant ) => { console.log("Track unsubscribed:", track.kind, "from", participant.identity); track.detach(); }); // Connect to room console.log("Connecting to LiveKit:", response.url); console.log("Room name:", response.room_name); await newRoom.connect(response.url, response.token, { rtcConfig: { iceServers: [ { urls: [ 'stun:stun.l.google.com:19302', 'stun:stun1.l.google.com:19302', ], }, { urls: 'turn:openrelay.metered.ca:80', username: 'openrelayproject', credential: 'openrelayproject', }, ], iceCandidatePoolSize: 10, iceTransportPolicy: 'all', }, autoSubscribe: true, }); console.log("Connected! Local participant:", newRoom.localParticipant.identity); // Enable microphone await newRoom.localParticipant.setMicrophoneEnabled(true); setRoom(newRoom); } catch (error: any) { console.error("Failed to connect:", error); let errorMessage = error.message; if (error.message?.includes("could not establish pc connection") || error.message?.includes("connection")) { errorMessage = "Cannot connect to LiveKit server. Please ensure:\n" + "1. LiveKit server is running and accessible\n" + "2. Check LIVEKIT_URL in .env file\n" + "3. Verify network connectivity to LiveKit server"; } sonnerToast.error(
Failed to connect: ${errorMessage}
, { duration: 10000, }); setIsConnecting(false); } }; const disconnectFromAgent = async () => { if (room) { await room.disconnect(); setRoom(null); setIsConnected(false); setRoomName(""); sonnerToast.info("Disconnected from agent"); } }; return ( <DashboardLayout title="Test Agent"> <div className="space-y-6"> {/* Header */} <div className="flex items-center justify-between"> <div className="flex items-center gap-3"> <div className="flex items-center justify-center w-12 h-12 rounded-lg bg-primary/10"> <MessageSquare className="w-6 h-6 text-primary" /> </div> <div> <h2 className="text-2xl font-bold">Test Agent</h2> <p className="text-sm text-muted-foreground"> Connect and chat with your voice agent </p> </div> </div> </div> {/* Connection Controls & Chat Interface */} <div className="grid grid-cols-1 gap-4"> {/* Connection Controls */} <Card> <CardHeader> <div className="flex items-center justify-between"> <div> <CardTitle className="flex items-center gap-2"> <MessageSquare className="w-5 h-5" /> Test Agent Chat </CardTitle> <CardDescription> Connect to your agent and test via text chat </CardDescription> </div> {isConnected && ( <Badge variant="default" className="bg-green-500"> Connected </Badge> )} {isConnecting && ( <Badge variant="secondary"> <Loader2 className="w-3 h-3 mr-1 animate-spin" /> Connecting... </Badge> )} </div> </CardHeader> <CardContent className="space-y-4"> <Alert> <AlertCircle className="h-4 w-4" /> <AlertDescription> {isConnected ?
Connected to room: ${roomName}. You can now chat with your agent via text messages.
: "Click 'Connect to Agent' to start testing. The agent will respond to your text messages with voice."} </AlertDescription> </Alert> <div className="flex gap-2"> {!isConnected ? ( <Button onClick={connectToAgent} disabled={isConnecting} className="w-full" > {isConnecting ? ( <> <Loader2 className="w-4 h-4 mr-2 animate-spin" /> Connecting... </> ) : ( <> <MessageSquare className="w-4 h-4 mr-2" /> Connect to Agent </> )} </Button> ) : ( <Button onClick={disconnectFromAgent} variant="destructive" className="w-full" > <PhoneOff className="w-4 h-4 mr-2" /> Disconnect </Button> )} </div> {/* Hidden audio container for agent voice responses */} <div ref={audioContainerRef} className="hidden" /> </CardContent> </Card> {/* Chat Interface */} <Card> <CardHeader> <CardTitle>Chat Messages</CardTitle> <CardDescription> Send text messages and receive voice responses from the agent </CardDescription> </CardHeader> <CardContent> <ChatInterface room={room} isConnected={isConnected} /> </CardContent> </Card> {/* Bidirectional Transcription */} {isConnected && roomName && ( <BidirectionalTranscription roomName={roomName} /> )} </div> </div> </DashboardLayout> ); }
t
Thanks — I reviewed your TestAgent code and the "could not establish pc connection" error. Quick, focused steps to try now (please try in order and tell me which step fails): 1. Run the LiveKit Connection Tester with your server URL + token: https://www.livekit.io/connection-test. Share whether WebSocket, WebRTC, or TURN fails. 2. Short network checks: ◦ Disable VPN/firewall temporarily or test from a home/mobile network. ◦ Try in an incognito window or a different browser (Chrome/Firefox). ◦ Ensure UDP is allowed on the network (corporate networks often block UDP). 1. Force TURN relay (if UDP blocked). Replace your connect call with:
Copy code
await newRoom.connect(response.url, response.token, {
  rtcConfig: {
    iceTransportPolicy: 'relay',
    iceServers: [ /* your TURN/STUN servers */ ],
  },
  autoSubscribe: true,
});
1. Confirm TURN/STUN servers are reachable and correctly configured (credentials, URL, ports). You’re using openrelay.metered.ca — ensure it’s reachable from the client network. 2. Verify server reachability and TLS: ◦ Ensure response.url uses wss/https and is the correct LIVEKIT_URL. ◦ If self-hosted, check the LiveKit server is running and TLS certs are valid. 1. Check browser/device permissions and attach logic: ◦ Ensure microphone permission is granted before calling
setMicrophoneEnabled(true)
. ◦ When
TrackSubscribed
attaches audio, ensure the audio container is visible (remove
className="hidden"
while testing). If you run the Connection Tester, paste its result here (WebSocket/WebRTC/TURN status). If tester shows WebRTC or TURN failing, tell me whether you can test from a different network and whether you want me to suggest a recommended TURN provider and exact rtcConfig values.
b
also if i tired calling livekit agent it doesn't start at first if retries 2 or 3 times after that only connected to livekt agent via frontend as well as via the sip
t
Thanks — I hear you: connection only succeeds after 2–3 retries and that’s causing frustration. Below are concise, prioritized steps and targeted diagnostic questions to find a quick fix. Immediate fixes to try (order matters) 1. Force TURN relay to avoid flaky UDP/ICE:
Copy code
await newRoom.connect(response.url, response.token, {
  rtcConfig: {
    iceTransportPolicy: 'relay',
    iceServers: [
      { urls: 'turn:<http://openrelay.metered.ca:80|openrelay.metered.ca:80>', username: 'openrelayproject', credential: 'openrelayproject' },
      // add any paid TURN provider you have
    ],
  },
  autoSubscribe: true,
});
1. Make the audio container visible while testing (remove
className="hidden"
) so attached audio plays reliably. 2. Delay enabling mic until after connect completes:
Copy code
newRoom.on(RoomEvent.Connected, async () => {
  await newRoom.localParticipant.setMicrophoneEnabled(true);
});
1. Reduce
iceCandidatePoolSize
(try 0 or 1) — large pools can increase initial ICE overhead in some networks. 2. Test from a different network (mobile hotspot) to rule out corporate/firewall UDP restrictions. Targeted diagnostics (please share results) • Run the LiveKit Connection Tester with your server URL + token and paste which stages fail: WebSocket / WebRTC / TURN. • When a connect attempt fails, paste the browser console logs for the first failed attempt (include any ICE/DTLS errors). • Are you using a self-hosted LiveKit server or LiveKit cloud? If self‑hosted, share server startup logs around the failed attempt. • What is the token TTL from your backend? (very short TTLs can cause retry timing issues) • Does the failure happen for all clients or only certain browsers/devices? Why these checks matter (brief) • Intermittent success usually points to ICE/TURN timing or network blocking (UDP/NAT) or token/handshake timing. • Forcing relay and delaying mic enable reduces timing-sensitive steps and often makes the first attempt succeed. If you share the Connection Tester result and the first-attempt console logs, I’ll give the exact next tweak (e.g., specific rtcConfig, recommended TURN provider, or server-side token timing fix).