RocketChat/Rocket.Chat · warning · ContactNotFoundError

error-contact-not-found

Error message

error-contact-not-found

What it means

In the Outbound Message wizard's RecipientForm, the contact is fetched via useQuery on omnichannelQueryKeys.contact(contactId). The queryFn calls getContact({ contactId }) and throws ContactNotFoundError (message 'error-contact-not-found', a FormFetchError subclass) when the returned contact is marked unknown — a stopgap until the endpoint itself handles unknown contacts. It puts the query in an error state so the form can validate the contact field.

Source

Thrown at apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/RecipientForm/RecipientForm.tsx:85

	const getContact = useEndpoint('GET', '/v1/omnichannel/contacts.get');
	const getProvider = useEndpoint('GET', '/v1/omnichannel/outbound/providers/:id/metadata', { id: providerId });

	const customActions = useMemo(() => renderActions?.({ isSubmitting }), [isSubmitting, renderActions]);

	const {
		data: contact,
		isError: isErrorContact,
		isSuccess: isSuccessContact,
		isFetching: isFetchingContact,
		refetch: refetchContact,
	} = useQuery({
		queryKey: omnichannelQueryKeys.contact(contactId),
		queryFn: async () => {
			const data = await getContact({ contactId });

			// TODO: Can be safely removed once unknown contacts handling is added to the endpoint
			if (data?.contact && data.contact.unknown) {
				throw new ContactNotFoundError();
			}

			return data;
		},
		staleTime: 5 * 60 * 1000,
		select: (data) => data?.contact || undefined,
		enabled: !!contactId,
	});

	const {
		data: provider,
		isError: isErrorProvider,
		isSuccess: isSuccessProvider,
		isFetching: isFetchingProvider,
		refetch: refetchProvider,
	} = useQuery({
		queryKey: omnichannelQueryKeys.outboundProviderMetadata(providerId),
		queryFn: () => getProvider(),

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Convert the unknown visitor into a registered contact first (Contacts UI or POST /omnichannel/contact), then reopen the wizard.
  2. If the contact was deleted or merged, re-select the recipient to obtain a fresh contactId.
  3. Code-level: key the form off isErrorContact to prompt contact creation instead of submitting.

Example fix

// before
if (data?.contact && data.contact.unknown) {
  throw new ContactNotFoundError();
}

// caller-side: block step advance while contact query is in error
if (isErrorContact) {
  validateContactField(true); // show field error, do not proceed
  return;
}
Defensive patterns

Strategy: validation

Validate before calling

const { contact } = await getContact({ contactId });
if (!contact || contact.unknown) {
  // convert/register the contact before opening the outbound wizard
}

Type guard

const isUsableContact = (c: IContact | undefined): c is IContact =>
  Boolean(c && !c.unknown && typeof c._id === 'string');

Prevention

When it happens

Trigger: Opening the outbound-message wizard with a contactId whose contact record has unknown: true (visitor not yet a registered contact), or a contactId that resolves to no usable contact object.

Common situations: Starting an outbound conversation from a visitor/lead that never became a full contact; stale contactId passed from a previous session; records created by integrations with unknown flag set.

Related errors


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