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
- Read the toast text — it is result.error verbatim; auth-related strings point to a session/permission problem.
- If the message looks like an auth error, force a re-login or re-fetch the auth token before retrying the upload.
- If a custom app intercepts the upload, check its handler returns a proper status; update it to set result.status for consistency.
- 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
- Refresh the auth token before retrying uploads after long idle periods.
- Ensure apps-engine upload interceptors populate both status and error.
- Log the raw response body server-side so empty statuses are diagnosable.
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.