RocketChat/Rocket.Chat · error · Error

not-found

Error message

not-found

What it means

MediaCallService.answerCall() verifies the incoming call exists and that the current user is its callee, via MediaCalls.findOneByIdAndCallee(callId, { type: 'user', id: uid }). 'not-found' means no call document matches that pair: the callId is wrong or expired, the call was already ended and removed, or the answering user is not the call's callee.

Source

Thrown at apps/meteor/server/services/media-call/service.ts:62

		this.onEvent('watch.settings', async ({ setting }): Promise<void> => {
			if (setting._id.startsWith('VoIP_TeamCollab_')) {
				setImmediate(() => this.configureMediaCallServer());
			}
		});

		this.configureMediaCallServer();
	}

	public async answerCall(uid: IUser['_id'], params: Omit<ClientMediaSignalAnswer, 'type'>): Promise<IMediaCall> {
		const { callId, answer } = params;

		const call = await MediaCalls.findOneByIdAndCallee<Pick<IMediaCall, '_id'>>(
			callId,
			{ type: 'user', id: uid },
			{ projection: { _id: 1 } },
		);
		if (!call) {
			throw new Error('not-found');
		}

		const signal: ClientMediaSignalAnswer = {
			type: 'answer',
			...params,
		};

		await callServer.receiveSignal(uid, signal, { throwIfSkipped: true });

		const updatedCall = await MediaCalls.findOneById(callId);
		if (!updatedCall) {
			throw new Error('internal-error');
		}

		switch (answer) {
			case 'ack':
				if (updatedCall.acceptedAt || updatedCall.ended) {
					throw new Error('invalid-call-state');

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Treat 'not-found' as terminal: dismiss the ringing UI and clear local call state — do not retry the same callId
  2. Before answering, re-sync the active call for this user and only answer the callId currently offered
  3. Verify the answering user id matches the call's callee (same account, not a second user of the device)
  4. De-duplicate answer submissions in the client so stale sessions cannot answer

Example fix

// before
await mediaCallService.answerCall(uid, { callId, answer: 'accept', /* ... */ });

// after
const call = await MediaCalls.findOneByIdAndCallee(callId, { type: 'user', id: uid }, { projections: { _id: 1 } });
if (!call) {
  // call gone (ended, expired, or addressed to someone else): clean up, do not answer
  return dismissRingingUI(callId);
}
await mediaCallService.answerCall(uid, { callId, answer: 'accept', /* ... */ });
Defensive patterns

Strategy: validation

Validate before calling

const call = await MediaCalls.findOneByIdAndCallee(callId, { type: 'user', id: uid }, { projections: { _id: 1 } });
if (!call) {
  // call ended, expired, or addressed to another user: do not send the answer
  return clearLocalCallState(callId);
}

Type guard

const isCallForUser = async (callId: string, uid: string): Promise<boolean> =>
  Boolean(await MediaCalls.findOneByIdAndCallee(callId, { type: 'user', id: uid }, { projections: { _id: 1 } }));

Try / catch

try {
  await mediaCallService.answerCall(uid, params);
} catch (err) {
  if (err instanceof Error && err.message === 'not-found') {
    // terminal: drop the stale callId and dismiss the ringing UI — never retry it
    return clearLocalCallState(params.callId);
  }
  throw err;
}

Prevention

When it happens

Trigger: Sending an answer signal with an expired or already-terminated callId; answering after the other side hung up (the MediaCalls document was deleted); answering from a user/session that is not the addressed callee.

Common situations: Client retries an answer after a network drop but the call timed out and was cleaned up; stale client call state after reconnect; race between caller cancelling and callee answering; duplicate ring/answer UI events.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/cd4f574cd13afd9a. Report an issue: GitHub.