RocketChat/Rocket.Chat · error · Error

message-not-found

Error message

message-not-found

What it means

Thrown by getPermaLink after it fails to find the message in both the local Messages store AND via a server REST API call to /v1/chat.getMessage. The function first checks the client cache (Messages.state.get), and if missing, calls getMessage which hits the API. If both return null/falsy, the message truly does not exist or the user lacks permission to read it.

Source

Thrown at apps/meteor/client/lib/getPermaLink.ts:24

	try {
		const { sdk } = await import('../../app/utils/client/lib/SDKClient');
		const { message } = await sdk.rest.get('/v1/chat.getMessage', { msgId });
		return message;
	} catch {
		return null;
	}
};

export const getPermaLink = async (msgId: string): Promise<string> => {
	if (!msgId) {
		throw new Error('invalid-parameter');
	}

	const { Messages, Rooms, Subscriptions } = await import('../stores');

	const msg = Messages.state.get(msgId) || (await getMessage(msgId));
	if (!msg) {
		throw new Error('message-not-found');
	}
	const roomData = Rooms.state.get(msg.rid);

	if (!roomData) {
		throw new Error('room-not-found');
	}

	const subData = Subscriptions.state.find((record) => record.rid === roomData._id && record.u._id === getUserId());

	const { roomCoordinator } = await import('./rooms/roomCoordinator');

	const roomURL = roomCoordinator.getURL(roomData.t, { ...(subData || roomData), tab: '' });
	return `${roomURL}?msg=${msgId}`;
};

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Catch the 'message-not-found' error in the caller and show a user-facing message like 'This message is no longer available'.
  2. Verify the message exists in the local cache before calling getPermaLink for messages the user is currently viewing.
  3. Check the user has access to the room containing the message before generating a permalink.
  4. Note that getMessage's internal catch block silently returns null on network errors — retry getPermaLink on transient failures.

Example fix

// before
try {
  const link = await getPermaLink(msgId);
} catch (e) { /* unhandled */ }
// after
try {
  const link = await getPermaLink(msgId);
} catch (e) {
  if (e instanceof Error && e.message === 'message-not-found') {
    dispatchToastMessage({ type: 'error', message: t('Message_not_available') });
    return;
  }
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

const msg = Messages.state.get(msgId);
if (!msg) {
  // optionally try API, or show 'not available'
  return;
}
const link = await getPermaLink(msgId);

Type guard

const messageExistsInCache = (msgId: string): boolean =>
  Boolean(Messages.state.get(msgId));

Try / catch

try {
  const link = await getPermaLink(msgId);
} catch (e) {
  if (e instanceof Error && e.message === 'message-not-found') {
    dispatchToastMessage({ type: 'error', message: t('Message_not_available') });
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: The message ID refers to a message that was deleted. The message exists but the user does not have read permission in the room. The message ID is valid but belongs to a room the user cannot access. Network error during the REST call causes getMessage to return null (it swallows errors in its catch block). Server is temporarily unreachable.

Common situations: Copying a link to a message that was deleted between page load and link generation. Message belongs to a private channel the user was removed from. Stale message ID from a bookmark or external link. Transient network failure during the API fallback.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12). Data as JSON: /api/errors/8096da246c9dd1e8. Report an issue: GitHub.