Problem/Motivation

In core/modules/content_moderation/src/Plugin/Field/FieldWidget/ModerationStateWidget.php, the formElement() method calls loadRevision() to retrieve the most recent saved revision of an existing entity, then immediately passes $original_entity to StateTransitionValidation::getValidTransitions() without first checking whether the revision load was successful.

If loadRevision() returns NULL (e.g. due to a missing, pruned, or otherwise unavailable revision), a fatal TypeError is thrown:

TypeError: Drupal\content_moderation\StateTransitionValidation::getValidTransitions():
Argument #1 ($entity) must be of type Drupal\Core\Entity\ContentEntityInterface, null given,
called in core/modules/content_moderation/src/Plugin/Field/FieldWidget/ModerationStateWidget.php
on line 144 in Drupal\content_moderation\StateTransitionValidation->getValidTransitions()
(line 43 of core/modules/content_moderation/src/StateTransitionValidation.php).

The code additionally contains a null check on $entity (not $original_entity) on line 143, placed after $entity has already been dereferenced multiple times. This check is effectively unreachable and does not protect against the actual failure point.

Steps to reproduce

  1. Enable content_moderation on a Drupal 11 site with a moderated content type.
  2. Create content of that type so that a revision record exists.
  3. Delete or otherwise invalidate the revision record from the database (this can occur via revision pruning, a failed deployment, or corrupted data) while leaving the node record intact.
  4. Attempt to edit the affected node.
  5. Observe the TypeError — the edit form cannot be rendered and the user sees a generic "temporary error" page.

Proposed resolution

Add a NULL check on $original_entity immediately after loadRevision() is called, before any further method calls or use as an argument. Return early with the unmodified $element if the revision cannot be loaded, consistent with how the method already handles non-moderated entities in form().

Additionally, remove the existing unreachable null check on $entity at line 143, which provides no defensive value and is misleading.

Remaining tasks

  • Review and confirm patch approach.
  • Add automated test coverage asserting that a node with an unloadable revision does not produce a fatal error when the edit form is rendered.

User interface changes

None.

Introduced terminology

None.

API changes

None.

Data model changes

None.

Release notes snippet

Fixed a fatal TypeError in ModerationStateWidget::formElement() that occurred when editing a moderated entity whose loaded revision ID could not be found in storage (e.g. after revision pruning or data corruption). The widget now returns gracefully instead of crashing with an unhandled exception.

Issue fork drupal-3578097

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

chamilsanjeewa created an issue. See original summary.

chamilsanjeewa’s picture

Status: Active » Closed (cannot reproduce)

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

nord102 made their first commit to this issue’s fork.

nord102’s picture

Status: Closed (cannot reproduce) » Needs review

Re-opening this

I ran into this issue on a Drupal 11.3.10 site after upgrading from D10. The TypeError fires during rebuildForm after the initial save of a new moderated node, getLoadedRevisionId() returns null on the form state entity during the rebuild, which gets passed directly to loadRevision() and then to getValidTransitions().

I also found a second instance of the same root cause in ModerationInformation::isFirstTimeModeration(), where getLatestRevisionId() can return null (when $entity->id() is null) and gets passed directly to loadRevision(), producing an array_flip() warning in ContentEntityStorageBase.

The fix guards both loadRevision() calls against null before invoking them. MR attached.

smustgrave’s picture

Version: 11.3.x-dev » main
Status: Needs review » Needs work
Issue tags: -content_moderation.module +Needs tests

Fixes need to be against main so changing versions

But will need test coverage to show the issue. But not sure about the fix, is it correct that null could be returned? Is this masking a larger issue.