RocketChat/Rocket.Chat · error · Meteor.Error
error-not-allowed
error-not-allowed
Error message
Not allowed
What it means
getS3FileUrl loads the upload via Uploads.findOneById(fileId) and throws error-not-allowed 'Not allowed' when the record is missing or has no rid — i.e. the file is not (yet) associated with any room. A second, later throw with the same code covers users who cannot access the file's room; this one at the rid check means the file itself is unusable for a redirect.
Source
Thrown at apps/meteor/server/meteor-methods/media/getS3FileUrl.ts:26
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
- Use the fileId exactly as returned by the completed upload (file record from the upload store), not a filename or external id
- Wait for the upload to finish (rid persisted) before requesting the S3 redirect URL
- If the record is gone, treat the file as deleted and stop resolving its URL
Example fix
// before
const url = await Meteor.callAsync('getS3FileUrl', fileId);
// after: server-side pre-check that the upload is room-bound
const file = await Uploads.findOneById(fileId);
if (!file?.rid) {
throw new Meteor.Error('error-not-allowed', 'File is missing or not attached to a room yet');
}
const url = await Meteor.callAsync('getS3FileUrl', file._id); Defensive patterns
Strategy: validation
Validate before calling
// server-side: verify the upload record is room-bound before requesting the URL
const file = await Uploads.findOneById(fileId);
if (!file?.rid) {
throw new Meteor.Error('error-not-allowed', 'File is missing or not attached to a room');
}
const url = await Meteor.callAsync('getS3FileUrl', file._id); Type guard
const isRoomBoundUpload = (f: { rid?: string } | null | undefined): f is { rid: string } =>
typeof f?.rid === 'string' && f.rid.length > 0; Try / catch
try {
const url = await Meteor.callAsync('getS3FileUrl', fileId);
} catch (err) {
if (err instanceof Meteor.Error && err.error === 'error-not-allowed') {
// either no rid on the file or no access to its room: stop resolving the URL
return;
}
throw err;
} Prevention
- Only request S3 URLs with the fileId returned by a completed upload
- Wait until the upload record has its rid before generating links
- Remember the same code also fires for users who cannot access the file's room
When it happens
Trigger: Passing a fileId that does not exist, belongs to a different collection, or was captured mid-upload before the room association (rid) was written; file ids copied from third-party asset references rather than the upload record.
Common situations: Clients resolving URLs before the upload transaction finishes; stale ids after files were purged; using ids from Uploads records that never got tied to a room.
Related errors
- error-invalid-user
- error-invalid-file-name
- error-invalid-characters-in-file-name
- error-invalid-data
- error-invalid-command
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/0ab044328d63b963.
Report an issue: GitHub.