Errors from custom FieldItemList::getConstraints() displayed next to every single property
We can place the errors with three modifications in the widget class.

First, we add the empty form element non_field_errors we can attach the errors to.

class YearRangeWidget extends WidgetBase {
    ...
    public function form($items, &amp;$form, $form_state, $get_delta = NULL) {
        $formElement = parent::form($items, $form, $form_state, $get_delta);
        $formElement['non_field_errors'] = ['#type' =&gt; 'item'];
        return $formElement;
    }
    ...
}


Second, in the flagErrors method, we collect all errors that do not belong to any property and call a separate method to treat them.

class YearRangeWidget extends WidgetBase {
    ...
    public function flagErrors($items, $violations, $form, $form_state) {
        $listViolations = [];
        foreach ($violations as $key =&gt; $violation) {
            if ($violation-&gt;getPropertyPath() === '') {
                $listViolations[] = $violation;
                $violations-&gt;remove($key);
            }
        }
        parent::flagErrors($items, $violations, $form, $form_state);
        $this-&gt;flagNonFieldErrors($items, $listViolations, $form, $form_state);
    }
    ...
}


Third, we define the method treating the separated errors.

class YearRangeWidget extends WidgetBase {
    ...
    public function flagNonFieldErrors($items, $violations, $form, $form_state) {
        $field = $items-&gt;getName();
        foreach ($violations as $violation) {
            $form_state-&gt;setError(
                $form[$field]['non_field_errors'],
                $violation-&gt;getMessage()
            );
        }
    }
    ...
}