Plugin version
grav-plugin-form 8.2.1 (and likely all versions since CaptchaManager was introduced)
Description
When captcha validation fails (e.g. basic-captcha, turnstile), CaptchaManager::validateCaptcha() fires the onFormValidationError event with only a top-level message string, but does not include a per-field messages array.
As a result:
form.messages[field.name] is always empty for captcha fields in Twig templates
- The field wrapper never receives the
has-errors CSS class (which is driven by form.messages[field.name])
- The
inline_errors: true form option has no visual effect on captcha fields — no error text appears under the field, and no red border is applied
All other field types validated via the standard Grav pipeline do populate form.messages correctly, so the problem is specific to the new CaptchaManager.
Steps to reproduce
- Add a
basic-captcha (or turnstile) field to a Grav form
- Enable
inline_errors: true on the form
- Submit the form with an incorrect captcha answer
- Observe: the top-level error message appears, but the captcha input field does not get the
has-errors class and shows no inline error
Root cause
In classes/Captcha/CaptchaManager.php, the onFormValidationError event is fired without a messages array:
// Current (broken) code — lines ~96–100
Grav::instance()->fireEvent('onFormValidationError', new Event([
'form' => $form,
'message' => $errorMessage,
// 'messages' key is missing!
'provider' => $providerName
]));
In form.php, onFormValidationError assigns $event['messages'] to $form->messages:
// form.php, onFormValidationError()
$form->status = 'error';
$form->message = $event['message'];
$form->messages = $event['messages']; // will be null because CaptchaManager didn't set it
In forms/default/field.html.twig, has-errors depends on form.messages[field.name]:
{% set errors = attribute(form.messages, field.name) %}
{%- if errors %}{% set form_field_outer_core = form_field_outer_core ~ ' has-errors' %}{% endif -%}
Since form.messages is null, errors is always falsy for captcha fields → no has-errors class.
For comparison, standard field validation in Form.php (line 950–951) correctly populates messages:
$this->messages = array_merge($this->messages, $e->getMessages());
$event = new Event(['form' => $this, 'message' => $this->message, 'messages' => $this->messages]);
Expected behaviour
CaptchaManager should pass a messages array in the event, mapping the captcha field name to the error message — exactly the same convention used by regular field validation:
Grav::instance()->fireEvent('onFormValidationError', new Event([
'form' => $form,
'message' => $errorMessage,
'messages' => [$captchaField['name'] => [$errorMessage]], // <-- fix
'provider' => $providerName
]));
This should be applied to both the !$result['success'] branch and the catch (\Exception $e) branch in validateCaptcha().
Workaround
Until this is fixed, the has-errors class can be forced in a theme-level Twig override of forms/default/field.html.twig:
{% set errors = attribute(form.messages, field.name) %}
{% if not errors and field.type == 'basic-captcha' and form.status == 'error' %}
{% set errors = [form.message] %}
{% endif %}
This is a hack and should not be necessary once CaptchaManager is corrected.
Plugin version
grav-plugin-form8.2.1 (and likely all versions sinceCaptchaManagerwas introduced)Description
When captcha validation fails (e.g.
basic-captcha,turnstile),CaptchaManager::validateCaptcha()fires theonFormValidationErrorevent with only a top-levelmessagestring, but does not include a per-fieldmessagesarray.As a result:
form.messages[field.name]is always empty for captcha fields in Twig templateshas-errorsCSS class (which is driven byform.messages[field.name])inline_errors: trueform option has no visual effect on captcha fields — no error text appears under the field, and no red border is appliedAll other field types validated via the standard Grav pipeline do populate
form.messagescorrectly, so the problem is specific to the newCaptchaManager.Steps to reproduce
basic-captcha(orturnstile) field to a Grav forminline_errors: trueon the formhas-errorsclass and shows no inline errorRoot cause
In
classes/Captcha/CaptchaManager.php, theonFormValidationErrorevent is fired without amessagesarray:In
form.php,onFormValidationErrorassigns$event['messages']to$form->messages:In
forms/default/field.html.twig,has-errorsdepends onform.messages[field.name]:{% set errors = attribute(form.messages, field.name) %} {%- if errors %}{% set form_field_outer_core = form_field_outer_core ~ ' has-errors' %}{% endif -%}Since
form.messagesisnull,errorsis always falsy for captcha fields → nohas-errorsclass.For comparison, standard field validation in
Form.php(line 950–951) correctly populatesmessages:Expected behaviour
CaptchaManagershould pass amessagesarray in the event, mapping the captcha field name to the error message — exactly the same convention used by regular field validation:This should be applied to both the
!$result['success']branch and thecatch (\Exception $e)branch invalidateCaptcha().Workaround
Until this is fixed, the
has-errorsclass can be forced in a theme-level Twig override offorms/default/field.html.twig:{% set errors = attribute(form.messages, field.name) %} {% if not errors and field.type == 'basic-captcha' and form.status == 'error' %} {% set errors = [form.message] %} {% endif %}This is a hack and should not be necessary once
CaptchaManageris corrected.