brief-evening-79958
12/25/2025, 1:20 PMtall-airline-16756
12/25/2025, 1:20 PMtall-airline-16756
12/25/2025, 1:20 PMawait 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 AWSbrief-evening-79958
12/25/2025, 1:21 PMDisconnected: ${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>
);
}tall-airline-16756
12/25/2025, 1:21 PMawait 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.brief-evening-79958
12/26/2025, 4:22 AMtall-airline-16756
12/26/2025, 4:22 AMawait 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:
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).