RocketChat/Rocket.Chat · error · Error
Invalid main message
Error message
Invalid main message
What it means
Thrown inside the threadMainMessage React Query queryFn when getMessage(tmid) returns a falsy value. This fetches the root/main message of a thread (identified by tmid — the thread message ID, which is the _id of the message that started the thread). If the fetch returns null/undefined, the query fails with 'Invalid main message', preventing the thread view from rendering with incomplete data.
Source
Thrown at apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadMainMessageQuery.ts:95
const queryClient = useQueryClient();
const unsubscribeRef = useRef<(() => void) | undefined>(undefined);
useEffect(() => {
return () => {
unsubscribeRef.current?.();
unsubscribeRef.current = undefined;
};
}, [tmid]);
return useQuery({
queryKey: roomsQueryKeys.threadMainMessage(room._id, tmid),
queryFn: async ({ queryKey }) => {
const mainMessage = await getMessage(tmid);
if (!mainMessage) {
throw new Error('Invalid main message');
}
const debouncedInvalidate = withDebouncing({ wait: 10000 })(() => {
queryClient.invalidateQueries({ queryKey, exact: true });
});
unsubscribeRef.current =
unsubscribeRef.current ||
subscribeToMessage(mainMessage, {
onMutate: async (message) => {
const msg = await onClientMessageReceived(message);
queryClient.setQueryData(queryKey, () => msg);
debouncedInvalidate();
},
onDelete: () => {
onDelete?.();
queryClient.invalidateQueries({ queryKey, exact: true });
},View on GitHub (pinned to f9d3ec372b)
Solutions
- Verify the thread main message exists and is accessible before opening the thread view.
- Catch the error in the query consumer and show a 'thread main message unavailable' state.
- Refresh thread metadata if the main message might have been deleted.
- Check user permissions on the thread's parent room before querying.
Example fix
// before
const { data } = useThreadMainMessageQuery({ room, tmid });
// after
const { data, isError } = useThreadMainMessageQuery({ room, tmid });
if (isError) {
return <ThreadUnavailable />;
} Defensive patterns
Strategy: try-catch
Validate before calling
const mainMessage = await getMessage(tmid);
if (!mainMessage) {
// do not open thread view, or show fallback
return;
} Type guard
const isThreadMainMessageValid = (msg: unknown): msg is IMessage => Boolean(msg && typeof msg === 'object' && '_id' in msg);
Try / catch
const { data, isError } = useThreadMainMessageQuery({ room, tmid });
if (isError) {
return <ThreadMainMessageUnavailable />;
} Prevention
- Verify the thread main message exists before opening the thread view.
- Handle the React Query error state with a user-facing fallback UI.
- Check user permissions on the parent room before querying thread data.
- Refresh thread metadata if the main message may have been deleted.
When it happens
Trigger: The thread's main message was deleted after the thread was created. The user does not have permission to read the main message. The tmid is invalid or refers to a message in a different room. Network error causes getMessage to return null. The main message is in a room the user left.
Common situations: Thread root message was deleted by a moderator but replies remain. User was removed from the room where the thread started. Stale tmid from cached thread metadata after room data changed. API returns null due to permission checks.
Related errors
- Message not found
- message-not-found
- error-pinning-message
- Invalid token for livechat message
- Invalid editor assigned to the message for the update.
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/02c1d96e92be27ae.
Report an issue: GitHub.