RocketChat/Rocket.Chat · error · Error

result.error

Error message

result.error

What it means

Thrown by useEndpointUploadMutation as the second-priority branch: the upload failed (success === false), there is no usable `status` string, but result.error is a string. The hook rethrows that string as the Error message so the server-supplied reason reaches the user toast. This path exists because some upload endpoints populate `error` instead of `status`.

Source

Thrown at apps/meteor/client/hooks/useEndpointUploadMutation.ts:27

	endpoint: TPathPattern,
	options?: UseEndpointUploadOptions<TData>,
) => {
	const sendData = useUpload(endpoint as PathFor<'POST'>);
	const dispatchToastMessage = useToastMessageDispatch();

	return useMutation({
		mutationFn: async (formData: FormData): Promise<TData> => {
			const data = sendData(formData);
			const promise = data instanceof Promise ? data : data.promise;
			const result = await promise;

			if (!result.success) {
				if (result.status) {
					throw new Error(result.status);
				}

				if (typeof result.error === 'string') {
					throw new Error(result.error);
				}

				throw new Error(t('FileUpload_Error'));
			}
			return result as TData;
		},
		onError: (error) => {
			dispatchToastMessage({ type: 'error', message: error });
		},
		...options,
	});
};

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Read the toast text — it is result.error verbatim; auth-related strings point to a session/permission problem.
  2. If the message looks like an auth error, force a re-login or re-fetch the auth token before retrying the upload.
  3. If a custom app intercepts the upload, check its handler returns a proper status; update it to set result.status for consistency.
  4. For legacy endpoints, verify the server version returns the UploadResult shape; upgrade the server if the error/status contract is missing.

Example fix

// The hook already branches on result.error; callers just need to handle it:
try {
  await upload.mutateAsync(formData);
} catch (e) {
  // e.message === the server's result.error string
  if (e.message.includes('logged in')) {
    redirectToLogin();
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure session is valid before uploading
if (!Meteor.userId() || !Meteor.loggedIn()) {
  throw new Error('Session expired');
}

Type guard

function hasStringError(r: UploadResult): r is UploadResult & { error: string } {
  return !r.success && typeof (r as any).error === 'string';
}

Try / catch

try {
  await upload.mutateAsync(formData);
} catch (e) {
  const msg = (e as Error).message; // equals result.error
  if (msg.includes('logged in')) { redirectToLogin(); }
}

Prevention

When it happens

Trigger: The upload endpoint returns a body such as { success: false, status: '', error: 'You must be logged in to do this' } or { success: false, error: '[error-not-allowed]' }. The `status` field is empty/undefined while `error` is a populated string, so control falls to the `typeof result.error === 'string'` branch.

Common situations: Session expired mid-upload so the endpoint returns an auth error string in `error` with an empty status; a custom apps-engine upload handler that rejects via throw new Meteor.Error with only a reason string; legacy upload routes that never populated the `status` field.

Related errors


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