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.

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.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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

Appendix: 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)