RocketChat/Rocket.Chat · error · Meteor.Error

invalid-file

invalid-file

Error message

invalid-file

What it means

Thrown by POST rooms.mediaConfirm/:rid/:fileId when no Uploads record matches the fileId, the calling user, AND the room simultaneously. The two-phase (Stream) upload flow stores the binary first and confirms later; this error means the confirmation step can't find that stored upload.

Source

Thrown at apps/meteor/server/api/v1/rooms.ts:338

				},
			});
		},
	},
);

API.v1.addRoute(
	'rooms.mediaConfirm/:rid/:fileId',
	{ authRequired: true },
	{
		async post() {
			if (!(await canAccessRoomIdAsync(this.urlParams.rid, this.userId))) {
				return API.v1.forbidden();
			}

			const file = await Uploads.findOneByIdAndUserIdAndRoomId(this.urlParams.fileId, this.userId, this.urlParams.rid);

			if (!file) {
				throw new Meteor.Error('invalid-file');
			}

			if ((this.bodyParams.description?.length ?? 0) > settings.get<number>('Message_MaxAllowedSize')) {
				throw new Meteor.Error('error-message-size-exceeded');
			}

			file.description = this.bodyParams.description;
			delete this.bodyParams.description;

			if (this.bodyParams.fileName) {
				file.name = this.bodyParams.fileName;
				delete this.bodyParams.fileName;
			}

			if (this.bodyParams.fileContent) {
				file.content = this.bodyParams.fileContent;
				delete this.bodyParams.fileContent;
			}

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Make sure the upload phase (rooms.upload / the Streamer/UFS flow) returned a fileId before calling mediaConfirm.
  2. Pass the same rid and the authenticated user that created the upload.
  3. Confirm promptly — do not cache fileIds across long-running sessions or user switches.
  4. If this fires, re-run the full two-phase upload from scratch.

Example fix

// before
await rest.post(`/api/v1/rooms.mediaConfirm/${rid}/${guessedFileId}`);

// after
const start = await startUploadPhase(rid, file); // returns fileId
await rest.post(`/api/v1/rooms.mediaConfirm/${rid}/${start.fileId}`, {
  description, fileName, fileContent,
});
Defensive patterns

Strategy: validation

Validate before calling

if (!fileId || typeof fileId !== 'string') {
  throw new Error('mediaConfirm requires a fileId from the upload phase');
}

Type guard

function isUploadRef(x: unknown): x is { fileId: string; rid: string } {
  return !!x && typeof (x as any).fileId === 'string' && typeof (x as any).rid === 'string';
}

Try / catch

try {
  await rest.post(`/api/v1/rooms.mediaConfirm/${rid}/${fileId}`, body);
} catch (e) {
  if (isMeteorError(e, 'invalid-file')) {
    // restart the full two-phase upload
  } else throw e;
}

Prevention

When it happens

Trigger: POST /api/v1/rooms.mediaConfirm/:rid/:fileId where the fileId was never created, belongs to a different user, belongs to a different room, or was already consumed/cleaned up before confirmation.

Common situations: Calling mediaConfirm before the upload phase completed; using a fileId from a different session/user; replaying a confirm after the upload expired; race with upload cleanup; wrong rid in the URL.

Related errors


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