File upload fields in the D8 Webform module don't show my custom 'required' message when they fail validation.

Here's the YAML source of a simple Webform:

  '#type': managed_file
  '#title': CV
  '#required': true
  '#required_error': 'Please upload a copy of your CV.'
  '#file_extensions': 'doc docx pdf odt rtf'
  '#type': document_file
  '#title': 'Test document'
  '#required': true
  '#required_error': 'Custom missing document file message.'
  '#format_items': comma

Instead of Please upload a copy of your CV etc., I get the default:

CV field is required.
Test document field is required.

I've run drush webform:repair
I've tested this on an otherwise empty form.
I've also cleared Drupal's cache.

Site setup:

Drupal 8.5.4.
No custom code yet.
(edit: we've since established it's nothing to do with contrib modules)

  • I was able to reproduce it, so it's a bug.
    – No Sssweat
    Commented Jun 19, 2018 at 3:16
  • I suggest you report it over at the issues page. The module's creator & maintainer jrockowitz is pretty quick at fixing them.
    – No Sssweat
    Commented Jun 19, 2018 at 7:57
  • Drupal core does not currently support the #required_error property for managed file uploads. @see \Drupal\file\Element\ManagedFile::validateManagedFile. We might need to remove this feature.
    – jrockowitz
    Commented Jun 19, 2018 at 12:51
  • Added my own answer, tidied question. Commented Jun 20, 2018 at 10:43

1 Answer 1


The error message for managed_file fields is added by ManagedFile::validateManagedFile - it does not support #required_error - the 'file missing' message (and others) are hardcoded:

      ->setError($element, t('@name field is required.', [
      '@name' => $element['#title'],

This is presumably because of the multiple types of error possible:

  • file does not exist
  • file is "not referenced" (it exists but is "unmanaged" - i.e. orphaned as far as DB concerned - see function docs linked above)
  • or, in our case, a file has not been uploaded at all


I bypassed this by adding my own validation to the form.

The Webform module has hook_webform_submission_form_alter - you can use this to append your own validation method:

function mymodule_webform_submission_form_alter(&$form, FormStateInterface $form_state, $form_id) {
    if ( $form['#webform_id'] != 'application' ) {
        // only do this for the specified form

    $form['#validate'][] = 'mymodule_fix_webform_validation';

function mymodule_fix_webform_validation($form, &$form_state) {

    // grab all the existing errors
    $errors = $form_state->getErrors();

    if ( isset( $errors['cv'] ) &&
         $errors['cv']->getUntranslatedString() == '@name field is required.' ) {
         // if (a) there's an error for this field, and
         // (b) it's the correct TYPE of error

         // reconstruct the error message
         // (you might add translatable strings if you need them)
         $errors['cv']->__construct( 'Please upload a copy of your CV.' );


(Decided to use the constructor from reading this answer to a similar question.)

Things I tried first:

The standard approach is to use $form_state->setError() or setErrorByName() - but neither appeared to have any effect on the error array - don't understand why.

On the actual form, the field was nested in a Webform "section"…


…so assumed I should supply the full array to setError(), for it to pull out the value of #parents and use that to construct the key. Except the error is actually stored as a flat 'cv' key, without the parent. Trying to set the 'cv' error using ByName didn't work either.

Overriding strings:

An alternative would be override the string (at time of writing, needs to be done programmatically: the string overrides module is not yet ready for D8). Add the following to settings.php:

$settings['locale_custom_strings_en'][''] = array(
    '@name field is required.' => 'Hello world!',

But this is a very broad brush and will apply to every file input on every form (plus anywhere else the phrase "@name field is required." is used - you can't specify the @name placeholder).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.