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

  1. Use the fileId exactly as returned by the completed upload (file record from the upload store), not a filename or external id
  2. Wait for the upload to finish (rid persisted) before requesting the S3 redirect URL
  3. 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

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


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/0ab044328d63b963. Report an issue: GitHub.