<p dir="auto">Symfony version im using 4.1.0</p>
<p dir="auto">During development of an app i was using form_row() to render each input with its label and errors, but when i tried to customize it a bit(i wanted the error message to be shown in the bottom of the input) i switched  to using form_label(),form_widget(),form_errors(). Once i test it i figured the error message has been duplicated(one above the input and one below most likely rendered by form_errors).<br>
(Yes i did cleared the cache.)</p>
<p dir="auto">SetupWizardType.php (form class)</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            -&gt;add('language', EntityType::class, [
                'translation_domain' =&gt; 'labels',
                'label' =&gt; 'label.languages',
                'placeholder' =&gt; 'label.choose',
                'class' =&gt; Languages::class
            ])
            -&gt;add('country', EntityType::class,[
                'translation_domain' =&gt; 'labels',
                'label' =&gt; 'label.countries',
                'placeholder' =&gt; 'label.choose',
                'class' =&gt; Countries::class
            ])
            -&gt;add('timezone', EntityType::class,[
                'translation_domain' =&gt; 'labels',
                'label' =&gt; 'label.timezones',
                'placeholder' =&gt; 'label.choose',
                'class' =&gt; Timezones::class
            ])
            -&gt;add('currency', EntityType::class,[
                'translation_domain' =&gt; 'labels',
                'label' =&gt; 'label.currencies',
                'placeholder' =&gt; 'label.choose',
                'class' =&gt; Currencies::class
            ]);

    }"><pre class="notranslate"><code class="notranslate">public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            -&gt;add('language', EntityType::class, [
                'translation_domain' =&gt; 'labels',
                'label' =&gt; 'label.languages',
                'placeholder' =&gt; 'label.choose',
                'class' =&gt; Languages::class
            ])
            -&gt;add('country', EntityType::class,[
                'translation_domain' =&gt; 'labels',
                'label' =&gt; 'label.countries',
                'placeholder' =&gt; 'label.choose',
                'class' =&gt; Countries::class
            ])
            -&gt;add('timezone', EntityType::class,[
                'translation_domain' =&gt; 'labels',
                'label' =&gt; 'label.timezones',
                'placeholder' =&gt; 'label.choose',
                'class' =&gt; Timezones::class
            ])
            -&gt;add('currency', EntityType::class,[
                'translation_domain' =&gt; 'labels',
                'label' =&gt; 'label.currencies',
                'placeholder' =&gt; 'label.choose',
                'class' =&gt; Currencies::class
            ]);

    }
</code></pre></div>
<p dir="auto">setup_wizard.twig.html</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="{{ form_start(form, {method: 'POST', action: path('wizard_index')}) }}

 {{ form_label(form.language) }}
 {{ form_widget(form.language, {attr: {'onchange': 'this.form.submit();'}}) }}
{{ form_errors(form.language) }}

{{ form_label(form.country) }}
{{ form_widget(form.country) }}
 {{ form_errors(form.country) }}

{{ form_label(form.timezone) }}
{{ form_widget(form.timezone) }}
{{ form_errors(form.timezone) }}

{{ form_label(form.currency) }}
{{ form_widget(form.currency) }}
 {{ form_errors(form.currency) }}
    &lt;div class=&quot;form-group&quot;&gt;
            &lt;button class=&quot;btn&quot;&gt;{% trans from 'buttons' %}button.prev_step{% endtrans %}&lt;/button&gt;
            &lt;button class=&quot;btn&quot;&gt;{% trans from 'buttons' %}button.next_step{% endtrans %}&lt;/button&gt;
            &lt;input class=&quot;btn&quot; type=&quot;submit&quot; name=&quot;save&quot; value=&quot;{% trans from 'buttons' %}button.save{% endtrans %}&quot; formnovalidate&gt;
                        &lt;/div&gt;

                    {{ form_end(form) }}"><pre class="notranslate"><code class="notranslate">{{ form_start(form, {method: 'POST', action: path('wizard_index')}) }}

 {{ form_label(form.language) }}
 {{ form_widget(form.language, {attr: {'onchange': 'this.form.submit();'}}) }}
{{ form_errors(form.language) }}

{{ form_label(form.country) }}
{{ form_widget(form.country) }}
 {{ form_errors(form.country) }}

{{ form_label(form.timezone) }}
{{ form_widget(form.timezone) }}
{{ form_errors(form.timezone) }}

{{ form_label(form.currency) }}
{{ form_widget(form.currency) }}
 {{ form_errors(form.currency) }}
    &lt;div class="form-group"&gt;
            &lt;button class="btn"&gt;{% trans from 'buttons' %}button.prev_step{% endtrans %}&lt;/button&gt;
            &lt;button class="btn"&gt;{% trans from 'buttons' %}button.next_step{% endtrans %}&lt;/button&gt;
            &lt;input class="btn" type="submit" name="save" value="{% trans from 'buttons' %}button.save{% endtrans %}" formnovalidate&gt;
                        &lt;/div&gt;

                    {{ form_end(form) }}
</code></pre></div>
<p dir="auto">If i remove every form_errors() , the message will show only once above the input, that's why i supposed it is rendered by form_widget.<br>
Any thoughts on this?</p>
<p dir="auto"><strong>Symfony version(s) affected</strong>: 3.4.11</p>
<p dir="auto">Hello,</p>
<p dir="auto"><code class="notranslate">@Assert\Valid</code> seems to work not the same way on a property, and on a getter.</p>
<p dir="auto">Here's the context :</p>
<p dir="auto">On a page, depending of a status, i need to apply different validations groups :<br>
<strong>Status</strong> | <strong>Validation Group</strong><br>
electronic | electronic<br>
full_paper | register<br>
half_paper | electronic &amp; register</p>
<p dir="auto">some validation should always be present, some only for electronic, some only for register, and when in "half_paper" status, every validation should be applied.</p>
<p dir="auto">I have 1 parent Entity with 2 children Entities, both in OneToMany.<br>
One <code class="notranslate">@Assert\Valid</code> is applied on the property for 1 children, and one <code class="notranslate">@Assert\Valid</code> is applied on a getter for the second children (because I need to distinguish by name some entities of the second children).<br>
Code :</p>
<p dir="auto"><strong>Parent</strong><br>
<code class="notranslate">Mandate.php</code></p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="    /**
     * @var Customer[]|Collection
     * @Assert\Valid(groups={&quot;electronic&quot;})
     * @Assert\Valid(groups={&quot;register&quot;})
     * @ORM\OneToMany(targetEntity=&quot;Customer&quot;, mappedBy=&quot;mandate&quot;, cascade={&quot;persist&quot;, &quot;remove&quot;})
     */
    protected $customers;

    /**
     * @ORM\OneToMany(targetEntity=&quot;Property&quot;, mappedBy=&quot;mandate&quot;, cascade={&quot;persist&quot;, &quot;remove&quot;})
     */
    protected $properties;

    /**
     * @Assert\Valid(groups={&quot;electronic&quot;})
     * @Assert\Valid(groups={&quot;register&quot;})
     */
    public function getMainProperty(): ?Property
    {
        foreach ($this-&gt;properties as $property) {
            if ($property-&gt;isMain()) {
                return $property;
            }
        }

        return null;
    }


    /**
     * @Assert\Valid(groups={&quot;electronic&quot;})
     * @Assert\Valid(groups={&quot;register&quot;})
     */
    public function getSecondaryProperties(): Collection
    {
        $properties = new ArrayCollection();
        foreach ($this-&gt;properties as $property) {
            if (!$property-&gt;isMain()) {
                $properties[] = $property;
            }
        }

        return $properties;
    }"><pre class="notranslate">    <span class="pl-c">/**</span>
<span class="pl-c">     * @var Customer[]|Collection</span>
<span class="pl-c">     * @Assert\Valid(groups={"electronic"})</span>
<span class="pl-c">     * @Assert\Valid(groups={"register"})</span>
<span class="pl-c">     * @ORM\OneToMany(targetEntity="Customer", mappedBy="mandate", cascade={"persist", "remove"})</span>
<span class="pl-c">     */</span>
    <span class="pl-k">protected</span> <span class="pl-s1"><span class="pl-c1">$</span>customers</span>;

    <span class="pl-c">/**</span>
<span class="pl-c">     * @ORM\OneToMany(targetEntity="Property", mappedBy="mandate", cascade={"persist", "remove"})</span>
<span class="pl-c">     */</span>
    <span class="pl-k">protected</span> <span class="pl-s1"><span class="pl-c1">$</span>properties</span>;

    <span class="pl-c">/**</span>
<span class="pl-c">     * @Assert\Valid(groups={"electronic"})</span>
<span class="pl-c">     * @Assert\Valid(groups={"register"})</span>
<span class="pl-c">     */</span>
    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">getMainProperty</span>(): ?<span class="pl-smi"><span class="pl-smi">Property</span></span>
    {
        <span class="pl-k">foreach</span> (<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">properties</span> <span class="pl-k">as</span> <span class="pl-s1"><span class="pl-c1">$</span>property</span>) {
            <span class="pl-k">if</span> (<span class="pl-s1"><span class="pl-c1">$</span>property</span>-&gt;<span class="pl-en">isMain</span>()) {
                <span class="pl-k">return</span> <span class="pl-s1"><span class="pl-c1">$</span>property</span>;
            }
        }

        <span class="pl-k">return</span> <span class="pl-c1">null</span>;
    }


    <span class="pl-c">/**</span>
<span class="pl-c">     * @Assert\Valid(groups={"electronic"})</span>
<span class="pl-c">     * @Assert\Valid(groups={"register"})</span>
<span class="pl-c">     */</span>
    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">getSecondaryProperties</span>(): <span class="pl-smi"><span class="pl-smi">Collection</span></span>
    {
        <span class="pl-s1"><span class="pl-c1">$</span>properties</span> = <span class="pl-k">new</span> <span class="pl-v">ArrayCollection</span>();
        <span class="pl-k">foreach</span> (<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">properties</span> <span class="pl-k">as</span> <span class="pl-s1"><span class="pl-c1">$</span>property</span>) {
            <span class="pl-k">if</span> (!<span class="pl-s1"><span class="pl-c1">$</span>property</span>-&gt;<span class="pl-en">isMain</span>()) {
                <span class="pl-s1"><span class="pl-c1">$</span>properties</span>[] = <span class="pl-s1"><span class="pl-c1">$</span>property</span>;
            }
        }

        <span class="pl-k">return</span> <span class="pl-s1"><span class="pl-c1">$</span>properties</span>;
    }</pre></div>
<p dir="auto">By the way, I'm forced to duplicate <code class="notranslate">@Assert\Valid</code> for every group, because if i merge groups in one <code class="notranslate">@Assert\Valid</code>, only the first group detected is applied on the childrens.</p>
<p dir="auto"><strong>Children</strong><br>
<code class="notranslate">Customer.php</code></p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="    /**
     * @Assert\NotBlank(groups={&quot;electronic&quot;, &quot;register&quot;})
     * @ORM\Column(type=&quot;string&quot;, nullable=true)
     */
    protected $firstName;

    /**
     * @Assert\NotBlank(groups={&quot;electronic&quot;})
     * @ORM\Column(type=&quot;string&quot;, nullable=true)
     */
    protected $email;

    /**
     * @Assert\NotBlank(groups={&quot;register&quot;})
     * @ORM\Column(type=&quot;string&quot;, nullable=true)
     */
    protected $city;"><pre class="notranslate">    <span class="pl-c">/**</span>
<span class="pl-c">     * @Assert\NotBlank(groups={"electronic", "register"})</span>
<span class="pl-c">     * @ORM\Column(type="string", nullable=true)</span>
<span class="pl-c">     */</span>
    <span class="pl-k">protected</span> <span class="pl-s1"><span class="pl-c1">$</span>firstName</span>;

    <span class="pl-c">/**</span>
<span class="pl-c">     * @Assert\NotBlank(groups={"electronic"})</span>
<span class="pl-c">     * @ORM\Column(type="string", nullable=true)</span>
<span class="pl-c">     */</span>
    <span class="pl-k">protected</span> <span class="pl-s1"><span class="pl-c1">$</span>email</span>;

    <span class="pl-c">/**</span>
<span class="pl-c">     * @Assert\NotBlank(groups={"register"})</span>
<span class="pl-c">     * @ORM\Column(type="string", nullable=true)</span>
<span class="pl-c">     */</span>
    <span class="pl-k">protected</span> <span class="pl-s1"><span class="pl-c1">$</span>city</span>;</pre></div>
<p dir="auto"><strong>Children</strong><br>
<code class="notranslate">Property.php</code></p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="    /**
     * @Assert\NotBlank(groups={&quot;electronic&quot;, &quot;register&quot;})
     * @ORM\Column(type=&quot;string&quot;, nullable=true)
     */
    protected $lotNumber;

    /**
     * @Assert\NotBlank(groups={&quot;electronic&quot;})
     * @ORM\Column(type=&quot;string&quot;, nullable=true)
     */
    protected $lotNature;

    /**
     * @Assert\NotBlank(groups={&quot;register&quot;})
     * @ORM\Column(type=&quot;string&quot;, nullable=true)
     */
    protected $programName;"><pre class="notranslate">    <span class="pl-c">/**</span>
<span class="pl-c">     * @Assert\NotBlank(groups={"electronic", "register"})</span>
<span class="pl-c">     * @ORM\Column(type="string", nullable=true)</span>
<span class="pl-c">     */</span>
    <span class="pl-k">protected</span> <span class="pl-s1"><span class="pl-c1">$</span>lotNumber</span>;

    <span class="pl-c">/**</span>
<span class="pl-c">     * @Assert\NotBlank(groups={"electronic"})</span>
<span class="pl-c">     * @ORM\Column(type="string", nullable=true)</span>
<span class="pl-c">     */</span>
    <span class="pl-k">protected</span> <span class="pl-s1"><span class="pl-c1">$</span>lotNature</span>;

    <span class="pl-c">/**</span>
<span class="pl-c">     * @Assert\NotBlank(groups={"register"})</span>
<span class="pl-c">     * @ORM\Column(type="string", nullable=true)</span>
<span class="pl-c">     */</span>
    <span class="pl-k">protected</span> <span class="pl-s1"><span class="pl-c1">$</span>programName</span>;</pre></div>
<p dir="auto">Here is how i validate :<br>
<code class="notranslate">MandateController.php</code></p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="    /**
     * @param ValidatorInterface $validator
     * @param Mandate $mandate
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function summaryAction(ValidatorInterface $validator, Mandate $mandate)
    {
       ...
      // $groups can be : ['electronic'], ['register'] or ['electronic', 'register'] 
        $errors = $validator-&gt;validate($mandate, null, $groups);
        ...
    }"><pre class="notranslate">    <span class="pl-c">/**</span>
<span class="pl-c">     * @param ValidatorInterface $validator</span>
<span class="pl-c">     * @param Mandate $mandate</span>
<span class="pl-c">     * @return \Symfony\Component\HttpFoundation\Response</span>
<span class="pl-c">     */</span>
    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">summaryAction</span>(<span class="pl-smi"><span class="pl-smi">ValidatorInterface</span></span> <span class="pl-s1"><span class="pl-c1">$</span>validator</span>, <span class="pl-smi"><span class="pl-smi">Mandate</span></span> <span class="pl-s1"><span class="pl-c1">$</span>mandate</span>)
    {
       <span class="pl-c1">.</span>.<span class="pl-c1">.</span>
      <span class="pl-c">// $groups can be : ['electronic'], ['register'] or ['electronic', 'register'] </span>
        <span class="pl-s1"><span class="pl-c1">$</span>errors</span> = <span class="pl-s1"><span class="pl-c1">$</span>validator</span>-&gt;<span class="pl-en">validate</span>(<span class="pl-s1"><span class="pl-c1">$</span>mandate</span>, <span class="pl-c1">null</span>, <span class="pl-s1"><span class="pl-c1">$</span>groups</span>);
        <span class="pl-c1">.</span>.<span class="pl-c1">.</span>
    }</pre></div>
<p dir="auto">Conclusion :</p>
<p dir="auto">Both group register &amp; electronic are applied on every customers, every fields having the "NotBlank" are triggered.<br>
<strong>BUT</strong> on properties, only 'electronic' is applied on every properties.<br>
It looks like this is very difficult to work with multi-groups in input on validate() method + multi-group on <code class="notranslate">@Assert</code> on entities. The merging flow looks like hard.</p>
<p dir="auto">My only solution right now is to work with only 1 group that I will call 'half_paper' and I will apply this group on <strong>every</strong> <code class="notranslate">@Assert</code> where electronic and/or register are present.</p>
<p dir="auto">Thanks</p>