melodic-zoo-29246
02/12/2026, 8:19 PM232 | const handler = room.registerTextStreamHandler('lk.transcription', async (reader, participantInfo) => {| ^ 233 | const participant = participantInfo?.identity || 'unknown'; 234 | let partialBuffer = ''; 235 | let currentSegmentId: string | null = null;
tall-airline-16756
02/12/2026, 8:19 PMtall-airline-16756
02/12/2026, 8:20 PMlk.transcription before your code ran.
To help you fix this quickly, I need a bit more context so I can recommend the best approach (reuse, replace, or fan-out). Can you answer the short list below?
1. Are you using any LiveKit UI libraries or components anywhere in your app (for example @livekit/components-react, useTranscriptions, or a prebuilt transcription component)?
2. Where are you calling room.registerTextStreamHandler('lk.transcription', ...) in your codebase — a single component, multiple components, or central initialization code? If possible, paste the surrounding code or describe when it runs (mount/unmount lifecycle).
3. Is your goal to replace an existing handler at runtime, or to have multiple independent consumers of the same transcription stream?
4. Which LiveKit SDK and components package versions are you using (for example livekit-client and @livekit/components-react versions)?
5. If you can, search your repo for other calls to registerTextStreamHandler('lk.transcription') and tell me whether any are found (or paste the search results).
What I’ll do next depending on your answers:
• If a LiveKit UI component (like useTranscriptions) is in use: I’ll show how to reuse that existing subscription instead of registering a new handler.
• If you need multiple consumers: I’ll suggest a single handler that fans out events internally (brief example).
• If you truly must swap handlers at runtime: I’ll explain safe unregister/register patterns or where to centralize registration so it only runs once.
Reply with those details and I’ll provide the specific code changes or examples you can apply.
Sources: Text and transcriptions | LiveKit Documentationmelodic-zoo-29246
02/12/2026, 8:21 PMtall-airline-16756
02/12/2026, 8:21 PMroom.registerTextStreamHandler('lk.transcription', ...) in your JS/TS code and whether you also have any Python services that might interact with LiveKit or your signaling layer.
Answering any of the above will let me recommend the next steps — for example, checking for duplicate handler registrations, reusing an existing handler, or confirming whether a Python service could be the source of the duplicate registration.melodic-zoo-29246
02/12/2026, 8:22 PMtall-airline-16756
02/12/2026, 8:23 PMlk.transcription (or lk.chat) and causing the conflict with the one you register in React Native.
What you can do next
1. Check the Python agent config/code for automatic text handling:
◦ If you’re using AgentSession or the LiveKit Python agents, they enable text handling and publish/subscribe to text streams by default (including lk.transcription for STT). Look for options like text_input or code paths that publish transcription or register handlers.
1. Disable or change the agent behavior if you don’t need it:
◦ Disable automatic text input handling in the agent (example: room_options=RoomOptions(text_input=False) when starting the session).
1. Use separate topics if both sides must handle text:
◦ If both frontend and agent need independent handlers, have one side use a custom topic (for example, custom.transcription) so there’s no collision.
1. Centralize handling and fan-out:
◦ Keep a single registered handler (either on the client or server) and have it dispatch events internally to multiple consumers. This avoids duplicate registrations while allowing multiple components to react to the same stream.
1. Quick repo checks to confirm the source:
◦ Search your backend for register_text_stream_handler, lk.transcription, or any code that publishes to lk.transcription.
◦ In React Native, confirm the handler registration is executed only once (e.g., in a top-level initialization or a component mount that runs once).
If you want, paste:
• The snippet where you start the Python agent/session (or the agent config) so I can point to the exact option to change.
• The React Native snippet where you call room.registerTextStreamHandler('lk.transcription', ...) and describe when it runs (component mount, navigation event, etc.).
With those two snippets I’ll recommend the exact change (disable agent handling, change topic, or centralize registration) and provide a small code example you can apply.
Sources: Text and transcriptions | LiveKit Documentationmelodic-zoo-29246
02/12/2026, 8:24 PMtall-airline-16756
02/12/2026, 8:24 PMroom.registerTextStreamHandler(...). Below are two safe patterns you can use depending on your app architecture.
Which to pick
• If you use LiveKit’s React components/hooks (or a library that already exposes transcription events), use that hook to read the transcription state. This avoids registering a handler.
• If you use the raw client in React Native and there isn’t a helper hook available, read transcription updates from the participant/room state or from text messages/events the backend publishes (instead of registering another handler). The backend’s existing handler can publish transcription segments into room state, participant metadata, or a separate custom text topic — you can subscribe to those without registering a conflicting handler.
Pattern A — Use a transcription hook (recommended if available) If you have access to a hook like useTranscriptions from your LiveKit React package, it already reads the transcription segments the agent publishes. This consumes the backend-created transcription stream without registering another handler.
Example (pseudo-React Native component using a transcription hook):
import React from 'react';
import { View, Text, FlatList } from 'react-native';
import { useTranscriptions } from '@livekit/components-react'; // adjust import to your library
export default function TranscriptionView() {
const transcriptions = useTranscriptions(); // read-only subscription
return (
<View style={{ flex: 1, padding: 12 }}>
<FlatList
data={transcriptions}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={{ marginBottom: 8 }}>
<Text style={{ fontWeight: '600' }}>
{item.participant?.identity ?? 'agent'}
</Text>
<Text>{item.text}</Text>
</View>
)}
ListEmptyComponent={<Text>No transcriptions yet</Text>}
/>
</View>
);
}
Notes:
• This does not call room.registerTextStreamHandler('lk.transcription', ...).
• Replace useTranscriptions import with the actual hook provided by your LiveKit React Native/React package. The hook reads whatever the backend publishes.
Pattern B — Read published updates from room/participant state or custom topic If a helper hook isn’t available, have the backend publish transcription segments somewhere your client can read without registering a handler. Common options:
• Backend appends transcription segments to an in-room data structure (e.g., participant metadata, a server-maintained store) and your client fetches/updates periodically or uses an existing event stream the backend already uses.
• Backend publishes to a custom text topic (not lk.transcription) and your client listens via existing UI-provided subscription utilities (not via registerTextStreamHandler).
Example approach using an event emitter or state sync (pseudo-code):
1) Backend: continues publishing lk.transcription (agent-created). Additionally, the backend writes transcription segments to a room-level store or emits them over an existing signaling channel your client can observe (no extra handler required on client).
2) React Native: subscribe to that existing room state or global event that the app already uses (WebSocket, Redux, or LiveKit-provided room events) to receive transcription updates.
Pseudo-code React Native component that consumes a shared transcription store:
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList } from 'react-native';
import { useRoom } from '@livekit/components-react'; // or your own room hook
// Or subscribe to your app's transcription store (e.g., Redux, Context, EventEmitter)
export default function TranscriptionView() {
const room = useRoom(); // optional, only if you have it
const [segments, setSegments] = useState([]);
useEffect(() => {
// Example: your app exposes an event emitter that backend updates
const onSegment = (segment) => {
setSegments((s) => [...s, segment]);
};
// Subscribe to your app-level transcription emitter
AppTranscriptionEmitter.addListener('segment', onSegment);
return () => {
AppTranscriptionEmitter.removeListener('segment', onSegment);
};
}, []);
return (
<View style={{ flex: 1, padding: 12 }}>
<FlatList
data={segments}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<>
<Text style={{ fontWeight: '600' }}>
{item.participant?.identity ?? 'agent'}
</Text>
<Text>{item.text}</Text>
</>
)}
/>
</View>
);
}
Practical next steps I can help with
• If you tell me which LiveKit React/React Native package and version you use, I’ll give the exact hook/import to use.
• If you want a small example where the backend writes segments to a room-level store or to a custom topic and the RN client consumes them, I can provide a concrete code pair (backend + client) showing that flow.
• If you prefer the simplest immediate step: search your app for any existing transcription hook (e.g., useTranscriptions) and use it instead of registerTextStreamHandler.
If you share which SDK/component version you’re on or paste your current RN snippet where you register the handler, I’ll return a tailored code example you can drop in to replace the handler registration.melodic-zoo-29246
02/12/2026, 8:26 PMuseTranscriptions only works in React. I need stuff that can work with react native expo SDK. Give me that code.tall-airline-16756
02/12/2026, 8:27 PMlk.transcription (the default), or does it also mirror transcription data to another channel the client can read without a text-stream handler — for example:
◦ room/participant metadata,
◦ LiveKit data messages on a different topic,
◦ or a separate custom text topic (not lk.transcription)?
If you’re not sure, paste the small Python snippet where the agent sends/exports transcriptions (or tell me whether it uses AgentSession defaults).
1. Which client packages and versions are you using in your Expo app? For example, the package.json entries for:
◦ livekit-client
◦ livekit-react-native (or any LiveKit wrapper)
If you don’t know versions, telling me the package names you installed is fine.
Why I need this
• If the agent already publishes to lk.transcription, the only way for the client to read those segments is via a text stream reader (which requires registerTextStreamHandler) or by the backend mirroring those segments to something the client can read without a handler (data messages, metadata, or a custom topic).
• If you cannot call registerTextStreamHandler at all, I’ll show a tiny backend change (example) to mirror transcriptions to data messages (or metadata) and then give an Expo client component to consume those (no text stream handler).
• If it’s acceptable to register one centralized handler in your Expo app (once, centrally), I can give a simple ready-to-drop component that registers once and fans out segments to the rest of your app.
Reply with the two answers and I’ll return the exact Expo-compatible code (client-only or client+backend example) you can drop into your app.
Sources: How to build an agent with speech-to-text input and text-only output | Live... | How to Set Up Speech-to-Text Input with Text-Only Output in LiveKit Agents | Text and transcriptions | LiveKit Documentation | Pipeline nodes and hooks | LiveKit Documentation | LiveKit coming to React Native!melodic-zoo-29246
02/12/2026, 8:28 PMtall-airline-16756
02/12/2026, 8:28 PMlivekit-client the client can only read lk.transcription via a text-stream reader, and your Python AgentSession is already publishing to lk.transcription. I can give you exact code next — but first one quick confirmation so I return the right implementation:
Please choose one of the two options below:
1) Client-only: I’ll provide a small Expo/React Native component that centrally registers a single room.registerTextStreamHandler('lk.transcription', ...) (safe, minimal change). This reads the agent’s transcriptions directly on the client. -or-
2) Backend-mirror: I’ll provide a tiny Python snippet to mirror each transcription segment into a channel the Expo app can read without a text-stream handler (for example: LiveKit data messages via room.sendData on a custom topic), plus the Expo client code to consume those messages.
If you choose option 2, tell me which mirror channel you prefer:
• LiveKit data messages on a custom topic (recommended), or
• participant/room metadata, or
• your own backend WebSocket/HTTP push endpoint.
Reply with your choice and (for option 2) the preferred mirror channel and I’ll send the exact, copy-pasteable Expo React Native code and any minimal Python snippet needed.