strapi/strapi · critical · TransferEngineInitializationError

Unresolved differences in schema [review workflows]

Error message

Unresolved differences in schema [review workflows]

What it means

Thrown by the CLI schema-diff handler (core/strapi/src/cli/utils/data-transfer.ts:587) during `strapi import` / restore integrity check. When diffing source vs destination schemas, review-workflows-related UIDs (plugin::review-workflows.workflow, plugin::review-workflows.workflow-stage, or any attribute whose path ends with strapi_stage / strapi_assignee) are tracked separately. If the diff kind is 'modified' (the schema SHAPE differs — a column type, attribute definition, or stage structure changed between the two sides) the handler raises a TransferEngineInitializationError (SeverityKind.FATAL) via engine.panic(). Unlike 'added'/'deleted' which only warn and can be overridden with --force / the confirm prompt, 'modified' is a hard stop that fires BEFORE the confirmMessage call, so it cannot be bypassed by --force.

Source

Thrown at packages/core/strapi/src/cli/utils/data-transfer.ts:587

            source
          );
        } else if (diff.kind === 'modified') {
          engine.reportWarning(chalk.red(`${chalk.bold(path)} has a different data type`), source);
        }
      }
    });

    // output the known feature warnings
    if (workflowsStatus === 'added') {
      engine.reportWarning(chalk.red(`Review workflows feature does not exist on source`), source);
    } else if (workflowsStatus === 'deleted') {
      engine.reportWarning(
        chalk.red(`Review workflows feature does not exist on destination`),
        source
      );
    } else if (workflowsStatus === 'modified') {
      engine.panic(
        new TransferEngineInitializationError('Unresolved differences in schema [review workflows]')
      );
    }

    const confirmed = await confirmMessage(
      'There are differences in schema between the source and destination, and the data listed above will be lost. Are you sure you want to continue?',
      {
        force,
      }
    );

    // reset handler back to normal
    setSignalHandler(() => abortTransfer({ engine, strapi: strapi as Core.Strapi }));

    if (confirmed) {
      context.ignoredDiffs = merge(context.diffs, context.ignoredDiffs);
    }

    return next(context);

View on GitHub (pinned to 4a4101264d)

Solutions

  1. Align the review-workflows plugin version (and Strapi version) between source and destination so the workflow / workflow-stage schemas are byte-identical, then regenerate the export and re-run import.
  2. If you do not need review workflows on this transfer, disable the review-workflows plugin on BOTH sides before exporting/importing — this moves the diff to 'added'/'deleted' (warn-only, overridable with --force).
  3. Inspect the actual modified attributes: dump `context.diffs` for the two review-workflows UIDs (or run import with STRAPI_DEBUG=true) to see which column (strapi_stage, strapi_assignee, etc.) changed type, then reconcile that column on the destination.
  4. On the destination, run `strapi cs:diff` (or compare content-types) against the source to confirm the review-workflows content types match before retrying the transfer.
  5. If the source is authoritative, restore the destination's review-workflows content type definitions to match the source (same attributes/types), restart Strapi so the DB schema syncs, then re-import.

Example fix

// before — both sides enabled, schema drifted (workflow-stage gained a column)
//   strapi import --file export.tar.gz
//   -> TransferEngineInitializationError: Unresolved differences in schema [review workflows]

// after — align versions / disable the plugin on both sides, then re-export & import
// 1) On source and destination, pin the same plugin version in package.json
//    "@strapi/plugin-review-workflows": "5.x.y"
// 2) OR disable on both before transfer:
//    config/plugins.ts -> 'review-workflows': { enabled: false }
// 3) Regenerate the export from source, then:
//    strapi import --file export.tar.gz
Defensive patterns

Strategy: validation

Validate before calling

// Before running `strapi import` / engine.transfer(), compare the
// review-workflows content-type schemas on source and destination. Abort early
// with an actionable message instead of letting the diff handler panic.
import { diff } from 'deep-diff';

const RW_UIDS = [
  'plugin::review-workflows.workflow',
  'plugin::review-workflows.workflow-stage',
];

function reviewWorkflowsSchemaIsCompatible(
  sourceSchemas: Record<string, Struct.Schema>,
  destSchemas: Record<string, Struct.Schema>
): { ok: true } | { ok: false; uids: string[] } {
  const modified: string[] = [];
  for (const uid of RW_UIDS) {
    const a = sourceSchemas[uid];
    const b = destSchemas[uid];
    if (a && b && diff(a, b)?.some((d) => d.kind === 'E' || d.kind === 'A')) {
      modified.push(uid);
    }
  }
  return modified.length === 0 ? { ok: true } : { ok: false, uids: modified };
}

const check = reviewWorkflowsSchemaIsCompatible(
  await sourceProvider.getSchemas() ?? {},
  await destinationProvider.getSchemas() ?? {}
);
if (!check.ok) {
  throw new Error(
    `Refusing to transfer: review-workflows schema differs on: ${check.uids.join(', ')}. ` +
    `Align the @strapi/plugin-review-workflows version on both sides, ` +
    `or disable the plugin on both sides before exporting.`
  );
}

Try / catch

// The CLI handler raises TransferEngineInitializationError via engine.panic during
// the integrity check, which propagates from engine.transfer(). Catch that class
// specifically and emit a schema-alignment hint instead of a raw stack trace.
import { TransferEngineInitializationError } from '@strapi/data-transfer/transfer';

try {
  await engine.transfer();
} catch (e) {
  if (e instanceof TransferEngineInitializationError &&
      /review workflows/i.test(e.message)) {
    console.error(
      'Import aborted: review-workflows schema differs between source and destination.\n' +
      'Options:\n' +
      '  1. Match the @strapi/plugin-review-workflows version on both environments and re-export.\n' +
      '  2. Disable review-workflows on BOTH source and destination, then re-export and re-import.'
    );
    process.exitCode = 1;
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Running `strapi import` (or a programmatic TransferEngine transfer using getDiffHandler) where both source and destination have the review-workflows plugin enabled, but the workflow / workflow-stage content type definitions differ. The diff loop sets workflowsStatus='modified' for any of: plugin::review-workflows.workflow, plugin::review-workflows.workflow-stage, or attributes strapi_stage* / strapi_assignee* whose diff.kind === 'modified'. engine.panic() then throws TransferEngineInitializationError during the initialization step.

Common situations: Source and destination run different versions of the review-workflows plugin (the workflow-stage schema gained/changed columns across releases). Review-workflows enabled on both Strapi environments but one has custom stages / stage schema customizations. Importing an export file produced by a newer Strapi into an older destination (or vice versa). Migrating between CE and EE where the review-workflows feature presence differs in shape rather than existence.

Related errors


AI-assisted analysis of strapi/strapi@4a4101264d (2026-08-12). Data as JSON: /api/errors/0cc54f97b7c162fb. Report an issue: GitHub.