enough-match-43171
02/19/2026, 3:17 PMtall-airline-16756
02/19/2026, 3:18 PMtall-airline-16756
02/19/2026, 3:18 PMroom_metadata_changed event and can read it via room.metadata.
Examples:
• Node.js
const { RoomServiceClient } = require('livekit-server-sdk');
const roomService = new RoomServiceClient('<https://your-livekit-url>', 'api-key', 'api-secret');
await roomService.updateRoomMetadata('my-room-name', 'New room metadata');
• Python
from livekit.api import LiveKitAPI
lk_api = LiveKitAPI(url='<https://your-livekit-url>', api_key='api-key', api_secret='api-secret')
await lk_api.room.update_room_metadata(room='my-room-name', metadata='New room metadata')
• Go
client := livekit.NewRoomServiceClient("<https://your-livekit-url>", "api-key", "api-secret")
_, err := client.UpdateRoomMetadata(context.Background(), &livekit.UpdateRoomMetadataRequest{
Room: "my-room-name",
Metadata: "New room metadata",
})
Important notes
• Updates are asynchronous. Clients should listen for room_metadata_changed to reliably receive the new value.
• Use room metadata for shared state (labels, workflow status, config, etc.).
• After calling the server API, do not assume immediate consistency — rely on the event to know when clients have the updated metadata.
If you want, tell me:
• Which server SDK and language you’re using, and
• Whether you want an example for listening to room_metadata_changed on the client side — I can provide that next. Sources: Handling asynchronous metadata updates in LiveKit | LiveKitenough-match-43171
02/19/2026, 3:20 PMtall-airline-16756
02/19/2026, 3:20 PMawait ctx.connect()).
Short answer: No — you do not need to open a LiveKit client session just to attach metadata when you create or update a room from the server. Creating the room with metadata via the Room Service API (or calling updateRoomMetadata) stores that string on the room immediately from the server side. Clients will receive the value when they join or when the metadata changes via the room_metadata_changed event.
A few practical points to keep in mind:
• The server-side call (your roomService.createRoom({... metadata: JSON.stringify(...)})) is sufficient to set the metadata; no client connection is required to persist it.
• Clients already connected will learn about updates by handling the room_metadata_changed event. New clients joining after creation will see room.metadata when they connect.
• If your server-side environment also needs to act as a participant (publish tracks, subscribe, or receive real-time events), then you would call connect() and create a participant session. Otherwise, for pure room management (create/update metadata, manage participants), server-side Room Service API calls are the correct approach.
• Metadata updates are effectively asynchronous across distributed SFU nodes — rely on the room_metadata_changed event on the client rather than assuming immediate propagation.
If you want, I can:
• Show a short client example that listens for room_metadata_changed, or
• Show how to update metadata later from the server with updateRoomMetadata.
Which would you prefer?enough-match-43171
02/19/2026, 6:43 PMimport json
from dotenv import load_dotenv
from livekit import agents
from livekit.agents import AgentServer, AgentSession, Agent, room_io, function_tool, get_job_context
from livekit.plugins import noise_cancellation, silero, google, deepgram, openai
from livekit.plugins.turn_detector.english import EnglishModel
from typing import List
from pydantic import BaseModel, Field
class Voice(BaseModel):
gender: str
voice_name: str = Field(alias="voiceName")
class RoomMetadata(BaseModel):
agent_id: str = Field(alias="agentId")
name: str
first_message: str = Field(alias="firstMessage")
instructions: str
voice: Voice
load_dotenv(".env")
class FrostlineAssistant(Agent):
def __init__(self, instructions: str, pid: str) -> None:
super().__init__(
instructions=instructions,
)
self.pid = pid
@function_tool
async def execute_flow(self, flow_id: List[str] | str) -> bool:
room = get_job_context().room
await room.local_participant.perform_rpc(
destination_identity=self.pid,
method="executeFlow",
payload=json.dumps({"flow_id": flow_id}),
)
return True
@function_tool
async def suggest_flow(self, flow_id: List[str] | str) -> bool:
room = get_job_context().room
await room.local_participant.perform_rpc(
destination_identity=self.pid,
method="suggestFlow",
payload=json.dumps({"flow_id": flow_id}),
)
return True
server = AgentServer()
@FlashCall Server.rtc_session()
async def frostline_agent(ctx: agents.JobContext):
await ctx.connect()
participant = await ctx.wait_for_participant()
room_metadata = RoomMetadata.model_validate_json(ctx.room.metadata or "{}", by_alias=True)
session = AgentSession(
stt=deepgram.STT(
model="nova-3"
),
llm=openai.LLM(
model="gpt-5.2",
),
tts=google.TTS(
gender=room_metadata.voice.gender,
voice_name=room_metadata.voice.voice_name,
),
vad=silero.VAD.load(),
turn_detection=EnglishModel(),
max_tool_steps=25,
user_away_timeout=10,
)
await session.start(
room=ctx.room,
agent=FrostlineAssistant(
instructions=room_metadata.instructions,
pid=participant.identity,
),
room_options=room_io.RoomOptions(
audio_input=room_io.AudioInputOptions(
noise_cancellation=noise_cancellation.BVC(),
),
),
)
await session.generate_reply(
instructions=room_metadata.first_message,
)
if __name__ == "__main__":
agents.cli.run_app(server)
const room = useRoomContext();
useEffect(() => {
room.registerRpcMethod("suggest_flow", async (data) => {
console.log("suggest_flow", data);
return "Flow suggested";
});
room.registerRpcMethod("execute_flow", async (data) => {
console.log("execute_flow", data);
return "Flow executed";
});
return () => {
room.unregisterRpcMethod("suggest_flow");
room.unregisterRpcMethod("execute_flow");
};
}, [room]);
m_rpc(\n ...<3 lines>...\n )\n File \"C:\\Users\\adity\\projects\\thundergen\\frostline-lk-agent\\.venv\\Lib\\site-packages\\livekit\\rtc\\participant.py\", line 328, in perform_rpc\n raise RpcError._from_proto(cb.perform_rpc.error)\nlivekit.rtc.rpc.RpcError: Method not supported at destination", "function": "suggest_flow", "speech_id": "speech_b7162c05c1b8", "timestamp": "2026-02-19T18:39:14.997085+00:00"}
{"message": "exception occurred while executing tool", "level": "ERROR", "name": "livekit.agents", "exc_info": "Traceback (most recent call last):\n File \"C:\\Users\\adity\\projects\\thundergen\\frostline-lk-agent\\.venv\\Lib\\site-packages\\livekit\\agents\\voice\\generation.py\", line 608, in _traceable_fnc_tool\n val = await function_callable()\n ^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"C:\\Users\\adity\\projects\\thundergen\\frostline-lk-agent\\main.py\", line 35, in execute_flow\n await room.local_participant.perform_rpc(\n ...<3 lines>...\n )\n File \"C:\\Users\\adity\\projects\\thundergen\\frostline-lk-agent\\.venv\\Lib\\site-packages\\livekit\\rtc\\participant.py\", line 328, in perform_rpc\n raise RpcError._from_proto(cb.perform_rpc.error)\nlivekit.rtc.rpc.RpcError: Method not supported at destination", "function": "execute_flow", "speech_id": "speech_b7162c05c1b8", "timestamp": "2026-02-19T18:39:16.032445+00:00"}
Why am i getting this RPC error, can you help me to debug it.tall-airline-16756
02/19/2026, 6:43 PMperform_rpc for suggestFlow / executeFlow.
What that error means
• The destination participant you targeted does not have the RPC method registered under the name you called. The call reached the target but the method was not found, so the target returned "Method not supported at destination."
Most likely causes and quick checks
1. Destination identity is wrong
◦ You set destination_identity=self.pid when calling perform_rpc. Confirm self.pid equals the participant.identity value from the client that registered the RPC methods.
◦ Log the participant identities on both sides:
▪︎ In your agent: print participant.identity when you call wait_for_participant() and before you call perform_rpc.
▪︎ In the client: print room.localParticipant.identity (or equivalent) after connecting.
◦ If identities don’t match, the RPC will go to a different participant (or no one), causing the error.
1. RPC method name mismatch
◦ On the client you register methods as "suggest_flow" and "execute_flow".
◦ In Python agent you call method names executeFlow and suggestFlow (note casing). The RPC system is case-sensitive and the destination expects the name you registered.
◦ Solution: Use the exact same RPC names on both sides. Either:
▪︎ Change server calls to match the client: use "suggest_flow" and "execute_flow", or
▪︎ Change client registration to camelCase ("suggestFlow" / "executeFlow").
◦ This naming mismatch is the most likely root cause based on your code.
1. Method registered on different participant
◦ Ensure the client that registered the RPC methods is the participant your agent is calling. If multiple participants exist, you may be targeting the wrong one.
◦ Use logs or a participant list to verify which participant registered the RPC methods.
1. Timing / ordering issue
◦ If the agent calls perform_rpc before the client has registered its RPC methods, the destination will not recognize the method.
◦ Ensure the client registers RPC handlers immediately after connecting and before you call perform_rpc. Add retries or a small delay, or implement a readiness handshake:
▪︎ Client emits a short RPC or a data message to tell the agent “ready”.
▪︎ Agent waits for that readiness signal before calling perform_rpc.
Actionable debugging steps
1. Log identities and registration:
◦ Agent: log participant.identity and the destination_identity you pass.
◦ Client: log its identity and confirm handlers registered (console log inside registration callback).
1. Match method names exactly (case-sensitive):
◦ Update either side so method names are identical. Example: change agent calls to:
await room.local_participant.perform_rpc(
destination_identity=self.pid,
method="suggest_flow",
payload=json.dumps({"flow_id": flow_id}),
)
• Or change client to register "suggestFlow" and "executeFlow".
1. Add a readiness handshake:
◦ Client sends a small RPC or data message to agent when handlers are registered.
◦ Agent waits for that signal before performing RPCs.
1. Retry with error details:
◦ Catch RpcError and log `cb.perform_rpc.error`/exception payload so you can see destination and method in logs.
If you’d like, I can:
• Provide the exact minimal code changes to make names consistent (Python agent example + client JS example), or
• Show a small readiness-handshake pattern to prevent timing issues.
Which would you prefer, and can you confirm whether self.pid equals the client participant identity and whether you want to change names to snake_case on the agent or camelCase on the client?