RocketChat/Rocket.Chat · error · Error

error-invalid-operation-status

Error message

error-invalid-operation-status

What it means

Rocket.Chat's import service drives a state machine: each Import document moves through statuses (uploading, preparing users, importer_user_selection, etc.) and ImportService.run() is the step that actually imports the selected users. run() only accepts the most recent operation when its status is exactly 'importer_user_selection'. This error means Imports.findLastImport() returned an operation in some other status, so the import cannot be started at this point in the flow.

Source

Thrown at apps/meteor/server/services/import/service.ts:162

					roles: data.roles ? [...new Set([...data.roles, ...defaultRoles])] : defaultRoles,
				},
				dataType: 'user',
				_updatedAt: new Date(),
			})),
		);

		await Imports.increaseTotalCount(operation._id, 'users', users.length);
		await Imports.setOperationStatus(operation._id, 'importer_user_selection');
	}

	public async run(userId: string): Promise<void> {
		const operation = await Imports.findLastImport();
		if (!operation?.valid) {
			throw new Error('error-operation-not-found');
		}

		if (operation.status !== 'importer_user_selection') {
			throw new Error('error-invalid-operation-status');
		}

		const { importerKey } = operation;
		const importer = Importers.get(importerKey);
		if (!importer) {
			throw new Error('error-importer-not-defined');
		}

		// eslint-disable-next-line new-cap
		const instance = new importer.importer(importer, operation, {
			skipUserCallbacks: true,
			skipDefaultChannels: true,
			enableEmail2fa: settings.get<boolean>('Accounts_TwoFactorAuthentication_By_Email_Auto_Opt_In'),
			quickUserInsertion: true,
			skipExistingUsers: true,
		});

		await instance.startImport({ users: { all: true } }, userId);

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Re-enter the import UI and complete the pending steps (upload, prepare, select users) so the operation status becomes 'importer_user_selection', then start the import
  2. Before calling run(), fetch the last operation via Imports.findLastImport() and verify status === 'importer_user_selection'; abort with a clear message otherwise
  3. If a stale/failed operation is stuck in the wrong status, delete the last Import record (or finish it through the UI) so a fresh import can be created
  4. Make the client idempotent: disable the start button after first submission to prevent duplicate run() calls

Example fix

// before
await importService.run(userId); // throws error-invalid-operation-status

// after
const operation = await Imports.findLastImport();
if (!operation?.valid || operation.status !== 'importer_user_selection') {
  throw new Error(`Import not ready to run (status: ${operation?.status ?? 'none'})`);
}
await importService.run(userId);
Defensive patterns

Strategy: validation

Validate before calling

import { Imports } from '@rocket.chat/models';

async function assertImportReadyToRun(): Promise<void> {
  const operation = await Imports.findLastImport();
  if (!operation?.valid) throw new Error('error-operation-not-found');
  if (operation.status !== 'importer_user_selection') {
    throw new Error(`Import not ready (current status: ${operation.status})`);
  }
}

Type guard

type ImportOperation = Awaited<ReturnType<typeof Imports.findLastImport>>;
const isReadyForUserImport = (op: ImportOperation): boolean =>
  !!op?.valid && op.status === 'importer_user_selection';

Try / catch

try {
  await importService.run(userId);
} catch (err) {
  if (err instanceof Error && err.message === 'error-invalid-operation-status') {
    // not retryable as-is: direct the user to finish the pending import step
    return notifyUser('Complete the import steps before starting');
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling the import start flow (ImportService.run(), the 'startImport' action behind the import wizard) when the last Import record's status is anything other than 'importer_user_selection' — e.g. while the file is still uploading, while users are still being prepared, after the import already ran and moved the status forward, or after an earlier stage failed.

Common situations: Double-submitting the start-import action (second call sees a changed status); skipping the user-selection screen; a previous failed import left the last operation stuck in an intermediate status; two browser tabs driving the same import.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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