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
- Treat 'not-found' as terminal: dismiss the ringing UI and clear local call state — do not retry the same callId
- Before answering, re-sync the active call for this user and only answer the callId currently offered
- Verify the answering user id matches the call's callee (same account, not a second user of the device)
- 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
- Expire ringing state client-side after the ring timeout instead of trusting old callIds
- Re-sync the active call after reconnect before sending any signal
- Never reuse callIds across sessions or tabs
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
- invalid-call-state
- internal-error
- invalid-calendar-event
- error-operation-not-found
- error-operation-not-found
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/cd4f574cd13afd9a.
Report an issue: GitHub.