Skip to content

Invalid state / migration not rolled back after validation issue in transformField #1515

@RodolpheGohard

Description

@RodolpheGohard

Hello, migrations will go in a corrupted state when there is a validation error during the transform entries phase.

I have a migration that looks like this:

module.exports.description = 'Update content model display names';

module.exports.up = (migration) => {
  const richLink = migration.editContentType('richLink')

  richLink.createField('internalName')
      .name('Name(internal)')
      .type('Symbol')
      .localized(false)
      .required(true)

  richLink.displayField('internalName')
  // ...

  migration.transformEntries({
    contentType: 'richLink',
    from: ['name'],
    to: ['internalName'],
    shouldPublish: 'preserve',
    transformEntryForLocale: async (fromFields, currentLocale, { id }) => {
      console.log(`transformEntry #${id} For Locale ${currentLocale}, with fields: ${JSON.stringify(fromFields)}`)
      return {
        internalName: (fromFields.name && fromFields.name[currentLocale]) || `untitled richlink ${id}`
      };
    },
  });
}

module.exports.down = (migration) => {
  const richLink = migration.editContentType('richLink')
      .displayField('name')
  richLink.deleteField('internalName')
}

When I run this migration, I have a case where some invalid entries (with fields not passing validation, easy to create with drafts) are causing the migration to fail:

Processing richLink
  up : 20250623195905-rich-link-content-name.js
transformEntry #4Q7REDACTED For Locale fr-FR, with fields: {"name":{"fr-FR":"Plus d'info"}}
transformEntry #4Q7REDACTED For Locale pl-PL, with fields: {"name":{"fr-FR":"Plus d'info"}}
transformEntry #5NmREDACTED For Locale fr-FR, with fields: {}
transformEntry #5NmREDACTED For Locale pl-PL, with fields: {}

# ... (REDACTED for concision)

The following migration has been planned
Environment: dev
Update Content Type richLink
  - displayField: "internalName"
  Create field internalName
    - name: "Name(internal)"
    - type: "Symbol"
    - localized: false
    - required: true
Publish Content Type richLink
Update field controls for Content Type richLink
  Update field internalName
    - widgetId: "singleLine"
    - widgetNamespace: "builtin"
    - helpText: "...REDACTED for concision"
Transform entries for richLink
  - from: name
  - to: internalName
[08:23:02] Update Content Type richLink [started]
[08:23:02] Making requests [started]
[08:23:02] Making requests (1/2) [title changed]
[08:23:02] → PUT /content_types/richLink at V15
[08:23:02] Making requests (2/2) [title changed]
[08:23:02] → PUT /content_types/richLink/published at V16
[08:23:03] Making requests (2/2) [completed]
[08:23:03] Update Content Type richLink [completed]
[08:23:03] Update field controls for Content Type richLink [started]
[08:23:03] Making requests [started]
[08:23:03] Making requests (1/1) [title changed]
[08:23:03] → PUT /content_types/richLink/editor_interface at V10
[08:23:03] Making requests (1/1) [completed]
[08:23:03] Update field controls for Content Type richLink [completed]
[08:23:03] Transform entries for richLink [started]
[08:23:03] Making requests [started]
[08:23:03] Making requests (1/27) [title changed]
[08:23:03] → PUT /entries/5sc8Y18Ae6A7Pb7gp3zpyi at V11
[08:23:03] Making requests (2/27) [title changed]
[08:23:03] → PUT /entries/5sc8Y18Ae6A7Pb7gp3zpyi/published at V12

# ... (REDACTED for concision)

[08:23:11] Making requests (27/27) [failed]
[08:23:11] → Batch failed
[08:23:11] Transform entries for richLink [failed]
[08:23:11] → Batch failed
🚨  Migration unsuccessful: 
Batch failed
Error: {"status":"Unprocessable Entity","message":"Validation error","details":{"errors":[{"name":"type","type":"Symbol","value":"/category/REDACTED","details":"Maximum Symbol length is 255 characters","path":["fields","link","fr-FR"]}]},"url":"https://api.contentful.com/spaces/REDACTED/environments/dev/entries/REDACTED"}
Please check the errors log for more details: /builds/invivodf/contentful/migration-contentful/errors-1751530982718.log
  error : Error: Batch failed
BatchError: Batch failed
    at Task.task (/builds/invivodf/contentful/migration-contentful/node_modules/contentful-migrate/lib/run.js:116:21)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  batch: {
    intent: EntryTransformIntent {
      type: 'contentType/transformEntries',
      meta: [Object],
      payload: [Object]
    },
    requests: [
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object]
    ],
    validationErrors: [],
    runtimeErrors: []
  },
  errors: [
    Error: {"status":"Unprocessable Entity","message":"Validation error","details":{"errors":[{"name":"type","type":"Symbol","value":"/category/REDACTED","details":"Maximum Symbol length is 255 characters","path":["fields","link","fr-FR"]}]},"url":"https://api.contentful.com/spaces/REDACTED/environments/dev/entries/REDACTED"}
        at /builds/invivodf/contentful/migration-contentful/node_modules/contentful-migrate/lib/run.js:111:36
        at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
        at async Task.task (/builds/invivodf/contentful/migration-contentful/node_modules/contentful-migrate/lib/run.js:102:15)
  ],
  context: [Object: null prototype] {}

The problem is, the schema alterations are not rollbacked, and yet the migration content is still at its previous. This cause subsequent migration run to enter a corrupt state, when it tries to run the contentModel migration again even though it has not been rollbacked.

I expect to the migration to be rollbacked, or there should be at least a flag that marks the last migration as failed and stop the contentful-migration from running it again.
Also, I expect drafts to not be validated, when shouldPublish is set to 'preserve' or false.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions