Fatal error "InvalidArgumentException: End date must not occur before start date"

Created on 29 July 2025, 2 months ago

Problem/Motivation

We've been getting a recurring issue where users become locked out of the form to create a node with a date_recur field due to this fatal error: "InvalidArgumentException: End date must not occur before start date"

The full stack trace is this:

"The website encountered an unexpected error. Try again later.

InvalidArgumentException: End date must not occur before start date. in Drupal\date_recur\DateRange->validateDates() (line 114 of modules/contrib/date_recur/src/DateRange.php).
Drupal\date_recur\DateRange->__construct(Object, Object) (Line: 32)
Drupal\date_recur\DateRecurNonRecurringHelper->__construct(Object, Object) (Line: 39)
Drupal\date_recur\DateRecurNonRecurringHelper::createInstance('', Object, Object) (Line: 452)
Drupal\date_recur\Plugin\Field\FieldType\DateRecurItem->getHelper() (Line: 138)
Drupal\date_recur_modular\Plugin\Field\FieldWidget\DateRecurModularWidgetBase->getRule(Object) (Line: 127)
Drupal\date_recur_modular\Plugin\Field\FieldWidget\DateRecurModularAlphaWidget->formElement(Object, 0, Array, Array, Object) (Line: 459)
Drupal\Core\Field\WidgetBase->formSingleElement(Object, 0, Array, Array, Object) (Line: 219)
Drupal\Core\Field\WidgetBase->formMultipleElements(Object, Array, Object) (Line: 120)
Drupal\Core\Field\WidgetBase->form(Object, Array, Object) (Line: 190)
Drupal\Core\Entity\Entity\EntityFormDisplay->buildForm(Object, Array, Object) (Line: 121)
Drupal\Core\Entity\ContentEntityForm->form(Array, Object) (Line: 134)
Drupal\node\NodeForm->form(Array, Object) (Line: 107)
Drupal\Core\Entity\EntityForm->buildForm(Array, Object)
call_user_func_array(Array, Array) (Line: 536)
Drupal\Core\Form\FormBuilder->retrieveForm('node_event_form', Object) (Line: 284)
Drupal\Core\Form\FormBuilder->buildForm(Object, Object) (Line: 48)
Drupal\Core\Entity\EntityFormBuilder->getForm(Object, 'default', Array) (Line: 392)
Drupal\group\Entity\Controller\GroupRelationshipController->createForm(Object, 'group_node:event')
call_user_func_array(Array, Array) (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 638)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 121)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array) (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 181)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 76)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 53)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 28)
Drupal\Core\StackMiddleware\ContentLength->handle(Object, 1, 1) (Line: 116)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 90)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 36)
Drupal\Core\StackMiddleware\AjaxPageState->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\StackedHttpKernel->handle(Object, 1, 1) (Line: 741)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)"

We are using date_recur in combination with group and date_recur_modular. You can see both of these are in the stack trace.

I believe the user becomes repeatedly locked out of the form by this error no matter how much they refresh the page because group uses the private tempstore to save the form state as part of a multi step form process. These rows in the key_value_expire table appear to be the culprit:

Because a row will be deleted from the key_value_expire table at most 7 days from when the row was added, this is a temporary problem for the user but still very frustrating. Unlike regular form validation they are never given a chance to correct their mistake.

Looking at \Drupal\date_recur\DateRange it's apparent that this class is working outside of the Form API. Why is that? Why should form validation throw an exception instead of using $form_state->setErrorByName()?

Steps to reproduce

I'm unsure exactly how to reproduce this and I suspect you won't be interested in installing other contrib modules to reproduce it. Still I think my concern about not working within Form API is valid.

Proposed resolution

Make \Drupal\date_recur\DateRange work within the Form API.

πŸ› Bug report
Status

Active

Version

3.8

Component

Code

Created by

πŸ‡ΊπŸ‡ΈUnited States maskedjellybean Portland, OR

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @maskedjellybean
  • πŸ‡ΊπŸ‡ΈUnited States maskedjellybean Portland, OR

    I've realized that the problem is not necessarily with \Drupal\date_recur\DateRange, but with the lack of form validation on the field widget which would prevent a user from encountering the exception.

    Luckily I'm using date_recur_modular and found a patch that adds that validation to the Alpha widget. I rerolled the patch and opened an MR there.
    https://www.drupal.org/project/date_recur_modular/issues/3071666#comment... πŸ› No form validation if start date is greater than end date Needs work

Production build 0.71.5 2024