RocketChat/Rocket.Chat · error · Meteor.Error
error-invalid-user
error-invalid-user
Error message
Invalid user
What it means
getS3FileUrl resolves the caller via Meteor.userId(); when the workspace setting FileUpload_ProtectFiles is enabled and there is no logged-in user, it throws error-invalid-user. The details object misleadingly says method 'sendFileMessage' — a copy-paste artifact; match on the code, not the method detail. The method then returns a redirect URL from the AmazonS3:Uploads store, which is why anonymous access is blocked when protection is on.
Source
Thrown at apps/meteor/server/meteor-methods/media/getS3FileUrl.ts:22
import { Meteor } from 'meteor/meteor';
import { canAccessRoomAsync } from '../../lib/authorization';
import { settings } from '../../settings';
import { UploadFS } from '../../ufs';
declare module '@rocket.chat/ddp-client' {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface ServerMethods {
getS3FileUrl(fileId: string): string;
}
}
Meteor.methods<ServerMethods>({
async getS3FileUrl(fileId) {
check(fileId, String);
const uid = Meteor.userId();
if (settings.get<boolean>('FileUpload_ProtectFiles') && !uid) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'sendFileMessage' });
}
const file = await Uploads.findOneById(fileId);
if (!file?.rid) {
throw new Meteor.Error('error-not-allowed', 'Not allowed');
}
const room = await Rooms.findOneById(file.rid);
if (uid && room && !(await canAccessRoomAsync(room, { _id: uid }))) {
throw new Meteor.Error('error-not-allowed', 'Not allowed');
}
return UploadFS.getStore('AmazonS3:Uploads').getRedirectURL(file);
},
});
View on GitHub (pinned to b2c16d5842)
Solutions
- Authenticate the client before requesting the URL
- For external sharing, use the signed/upload token returned by the upload flow (file link tokens) rather than this method
- Only if anonymous access is intentionally public, disable FileUpload_ProtectFiles — understand this removes protection for all uploads
Example fix
// before (anonymous call on a protected workspace)
const url = Meteor.call('getS3FileUrl', fileId);
// after
if (!Meteor.userId()) {
// settings.get('FileUpload_ProtectFiles') is true: login first
throw new Meteor.Error('error-invalid-user', 'Login required');
}
const url = await Meteor.callAsync('getS3FileUrl', fileId); Defensive patterns
Strategy: validation
Validate before calling
if (!Meteor.userId()) {
// FileUpload_ProtectFiles blocks anonymous getS3FileUrl: login first
throw new Meteor.Error('error-invalid-user', 'Login required');
}
const url = await Meteor.callAsync('getS3FileUrl', fileId); Try / catch
try {
const url = await Meteor.callAsync('getS3FileUrl', fileId);
} catch (err) {
if (err instanceof Meteor.Error && err.error === 'error-invalid-user') {
// protected files need a session: authenticate and retry once
return;
}
throw err;
} Prevention
- On protected workspaces, never resolve S3 URLs anonymously
- Use the file-link tokens from the upload response for external sharing
- Ignore the misleading 'sendFileMessage' method detail when matching this error
When it happens
Trigger: Calling getS3FileUrl(fileId) from an anonymous/guest context while FileUpload_ProtectFiles = true; also DDP calls after session expiry on protected workspaces.
Common situations: Embedding or hot-linking S3-hosted uploads in external pages or scripts without a session; enabling FileUpload_ProtectFiles on a workspace whose consumers were previously anonymous.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/cf153981a5c77648.
Report an issue: GitHub.