Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/catch-avatar-clear-buffer-rpc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@livekit/agents': patch
---

Handle rejected avatar clear-buffer RPCs without emitting unhandled promise rejections.
48 changes: 48 additions & 0 deletions agents/src/voice/avatar/datastream_io.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2026 LiveKit, Inc.
//
// SPDX-License-Identifier: Apache-2.0
import type { Room } from '@livekit/rtc-node';
import { describe, expect, it, vi } from 'vitest';
import { DataStreamAudioOutput } from './datastream_io.js';

const { logger } = vi.hoisted(() => ({
logger: {
debug: vi.fn(),
warn: vi.fn(),
},
}));

vi.mock('../../log.js', () => ({
log: () => logger,
}));

function createRoom(performRpc: () => Promise<string>) {
const avatar = { identity: 'avatar' };

return {
isConnected: true,
localParticipant: {
performRpc: vi.fn(performRpc),
registerRpcMethod: vi.fn(),
},
remoteParticipants: new Map([[avatar.identity, avatar]]),
on: vi.fn(),
off: vi.fn(),
};
}

describe('DataStreamAudioOutput.clearBuffer', () => {
it('handles a rejected clear-buffer RPC', async () => {
const room = createRoom(() => Promise.reject(new Error('Failed to send')));
const output = new DataStreamAudioOutput({
room: room as unknown as Room,
destinationIdentity: 'avatar',
});

await vi.waitFor(() => {
output.clearBuffer();
expect(room.localParticipant.performRpc).toHaveBeenCalledOnce();
});
await vi.waitFor(() => expect(logger.warn).toHaveBeenCalledOnce());
});
});
17 changes: 12 additions & 5 deletions agents/src/voice/avatar/datastream_io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,18 @@ export class DataStreamAudioOutput extends AudioOutput {
clearBuffer(): void {
if (!this.started) return;

this.room.localParticipant!.performRpc({
destinationIdentity: this.destinationIdentity,
method: RPC_CLEAR_BUFFER,
payload: '',
});
void this.room
.localParticipant!.performRpc({
destinationIdentity: this.destinationIdentity,
method: RPC_CLEAR_BUFFER,
payload: '',
})
.catch((error) => {
this.#logger.warn(
Comment thread
smorimoto marked this conversation as resolved.
{ error, destinationIdentity: this.destinationIdentity },
'failed to perform clear buffer rpc',
);
});
}

private handlePlaybackFinished(data: RpcInvocationData): string {
Expand Down
Loading