<p dir="auto">I have a user entity, which has a password field with several validation constraints attached.</p>
<p dir="auto">If some of my validation constraint failed, the error message got assigned to the parent form, which should not happen, as the error_bubbling is turned off.</p>
<p dir="auto">While tracking the problem down, I've came upon this conclusion:</p>
<p dir="auto">Since it's a registration process, I would like the user to confirm the password he types in order to avoid any mistyping:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="$builder-&gt;add('password', 'repeated', array(
    'type' =&gt; 'password',
    'invalid_message' =&gt; 'The password fields must match.',
    'options' =&gt; array(
        'label' =&gt; 'label.password',
        'attr'  =&gt; array(
            'class' =&gt; 'large_width valid',
        )
    ),
    'error_bubbling' =&gt; false,
));"><pre class="notranslate"><code class="notranslate">$builder-&gt;add('password', 'repeated', array(
    'type' =&gt; 'password',
    'invalid_message' =&gt; 'The password fields must match.',
    'options' =&gt; array(
        'label' =&gt; 'label.password',
        'attr'  =&gt; array(
            'class' =&gt; 'large_width valid',
        )
    ),
    'error_bubbling' =&gt; false,
));
</code></pre></div>
<p dir="auto">I also have some validation constraints about the size of the password and its complexity inside the validation.yml file:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="- NotBlank: { groups: [change_password, registration] }
- MinLength: { limit: 6, groups: [change_password, registration] }
- MaxLength: { limit: 200, groups: [change_password, registration ] }"><pre class="notranslate"><code class="notranslate">- NotBlank: { groups: [change_password, registration] }
- MinLength: { limit: 6, groups: [change_password, registration] }
- MaxLength: { limit: 200, groups: [change_password, registration ] }
</code></pre></div>
<p dir="auto">Now, the problem is that the error messages from any validation constraints are not processed as it should - instead of being assigned to the element, they are assigned directly to its form. The reason for this follows:</p>
<p dir="auto">Symfony\Component\Form\Extension\Validator\Validator\DelegatingValidator:80</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="foreach ($violations as $violation) {
    $propertyPath = $violation-&gt;getPropertyPath();
    $template = $violation-&gt;getMessageTemplate();
    $parameters = $violation-&gt;getMessageParameters();
    $error = new FormError($template, $parameters);

    foreach ($mapping as $mappedPath =&gt; $child) {
        if (preg_match($mappedPath, $propertyPath)) {
            $child-&gt;addError($error);
            continue 2;
        }
    }

    $form-&gt;addError($error);
}"><pre class="notranslate"><code class="notranslate">foreach ($violations as $violation) {
    $propertyPath = $violation-&gt;getPropertyPath();
    $template = $violation-&gt;getMessageTemplate();
    $parameters = $violation-&gt;getMessageParameters();
    $error = new FormError($template, $parameters);

    foreach ($mapping as $mappedPath =&gt; $child) {
        if (preg_match($mappedPath, $propertyPath)) {
            $child-&gt;addError($error);
            continue 2;
        }
    }

    $form-&gt;addError($error);
}
</code></pre></div>
<p dir="auto">In my case, I'm using a main form which has a subform - the one with the user entity attached, thus the $propertyPath value:<br>
<code class="notranslate">$propertyPath = "children[user].data.password"</code></p>
<p dir="auto">The important checked paths are:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="&quot;/^children\.data\.(?!\w)/&quot;
&quot;/^children\[user\]\.data\.(?!\w)/&quot;
&quot;/^children\[user\]\.data\.username(?!\w)/&quot;
&quot;/^children\[user\]\[password\]\.data\.(?!\w)/&quot;
&quot;/^children\[user\]\[password\]\.data\.first(?!\w)/&quot;
&quot;/^children\[user\]\[password\]\.data\.second(?!\w)/&quot;
&quot;/^children\[user\]\[password\](?!\w)/&quot;
.. some other fields
&quot;/^children\[user\](?!\w)/&quot;"><pre class="notranslate"><code class="notranslate">"/^children\.data\.(?!\w)/"
"/^children\[user\]\.data\.(?!\w)/"
"/^children\[user\]\.data\.username(?!\w)/"
"/^children\[user\]\[password\]\.data\.(?!\w)/"
"/^children\[user\]\[password\]\.data\.first(?!\w)/"
"/^children\[user\]\[password\]\.data\.second(?!\w)/"
"/^children\[user\]\[password\](?!\w)/"
.. some other fields
"/^children\[user\](?!\w)/"
</code></pre></div>
<p dir="auto">Note that the user.password.first and user.password.second fields are being checked, but not user.password.<br>
Perhaps this case should be added as well:<br>
<code class="notranslate">"/^children\[user\]\.data\.password(?!\w)/"</code></p>
<p dir="auto">The last one matched, which obviously is not correct. The reason for the issue seems like that either the repeat form element type was not designed to work with this, either this class was not written with having the repeat field in mind.</p>
<p dir="auto">When RepeatedType is nested inside another form, error messages such as "This value should not be blank" bubble up to the root form, instead of appearing above the field in the nested form.</p>
<p dir="auto">To illustrate this issue, I have put together the following example:</p>
<ul dir="auto">
<li>Two types where one is nested in the other (child inside parent).</li>
<li>Both types have the RepeatedType field.</li>
<li>Both RepeatedType fields are NotBlank.</li>
</ul>
<p dir="auto">As you can see, the error message for the RepeatedType in the main type, appears right above the fields, but this doesn't happen for the nested type.</p>
<p dir="auto"><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/10af27d7464a434029fe17612d696887a04771579701b22f03794d512d3f4888/68747470733a2f2f6c68342e676f6f676c6575736572636f6e74656e742e636f6d2f2d4a2d66435f5353494c714d2f546b6838486b61464139492f41414141414141414142672f726852505035326e697a512f73637265656e73686f742e706e67"><img src="https://camo.githubusercontent.com/10af27d7464a434029fe17612d696887a04771579701b22f03794d512d3f4888/68747470733a2f2f6c68342e676f6f676c6575736572636f6e74656e742e636f6d2f2d4a2d66435f5353494c714d2f546b6838486b61464139492f41414141414141414142672f726852505035326e697a512f73637265656e73686f742e706e67" alt="Screenshot" data-canonical-src="https://lh4.googleusercontent.com/-J-fC_SSILqM/Tkh8HkaFA9I/AAAAAAAAABg/rhRPP52nizQ/screenshot.png" style="max-width: 100%;"></a></p>
<p dir="auto">Here are the types and models that can be used to demonstrate the issue:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="class RegistrationParent
{
    public $registrationChild;

    /**
     * @Assert\NotBlank()
     */
    public $plainPassword;
}


class RegistrationChild
{
    /**
     * @Assert\NotBlank()
     */
    public $plainPassword;
}


class RegistrationParentFormType extends AbstractType
{
   public function buildForm(FormBuilder $builder, array $options)
   {
   $builder
      -&gt;add('registrationChild', new RegistrationChildFormType())
      -&gt;add('plainPassword', 'repeated', array(
         'type' =&gt; 'password',
      ))
   ;
 }


class RegistrationChildFormType extends AbstractType
{
   public function buildForm(FormBuilder $builder, array $options)
   {
      $builder
         -&gt;add('plainPassword', 'repeated', array(
            'type' =&gt; 'password',
         ))
      ;
 }"><pre class="notranslate"><code class="notranslate">class RegistrationParent
{
    public $registrationChild;

    /**
     * @Assert\NotBlank()
     */
    public $plainPassword;
}


class RegistrationChild
{
    /**
     * @Assert\NotBlank()
     */
    public $plainPassword;
}


class RegistrationParentFormType extends AbstractType
{
   public function buildForm(FormBuilder $builder, array $options)
   {
   $builder
      -&gt;add('registrationChild', new RegistrationChildFormType())
      -&gt;add('plainPassword', 'repeated', array(
         'type' =&gt; 'password',
      ))
   ;
 }


class RegistrationChildFormType extends AbstractType
{
   public function buildForm(FormBuilder $builder, array $options)
   {
      $builder
         -&gt;add('plainPassword', 'repeated', array(
            'type' =&gt; 'password',
         ))
      ;
 }
</code></pre></div>