Problem/Motivation
Example stack trace:
Warning: Undefined array key "aria-describedby" in webform_process_options() (line 1093 of .../modules/contrib/webform/webform.module)
#0 .../core/includes/bootstrap.inc(164): _drupal_error_handler_real()
#1 .../modules/contrib/webform/webform.module(1093): _drupal_error_handler()
#2 [internal function]: webform_process_options()
#3 .../core/lib/Drupal/Core/Form/FormBuilder.php(1025): call_user_func_array()
#4 .../core/lib/Drupal/Core/Form/FormBuilder.php(1088): Drupal\Core\Form\FormBuilder->doBuildForm()
#5 .../core/lib/Drupal/Core/Form/FormBuilder.php(1088): Drupal\Core\Form\FormBuilder->doBuildForm()
#6 .../core/lib/Drupal/Core/Form/FormBuilder.php(579): Drupal\Core\Form\FormBuilder->doBuildForm()
#7 .../core/lib/Drupal/Core/Form/FormBuilder.php(325): Drupal\Core\Form\FormBuilder->processForm()
#8 .../core/lib/Drupal/Core/Entity/EntityFormBuilder.php(48): Drupal\Core\Form\FormBuilder->buildForm()
#9 .../modules/contrib/webform/src/Entity/Webform.php(1257): Drupal\Core\Entity\EntityFormBuilder->getForm()
#10 .../modules/contrib/webform/src/Element/Webform.php(112): Drupal\webform\Entity\Webform->getSubmissionForm()
#11 [internal function]: Drupal\webform\Element\Webform::preRenderWebformElement()
Steps to reproduce
- Go to
admin/config/people/accounts - Navigate to REGISTRATION AND CANCELLATION > When cancelling a user account
- Inspect the radio buttons. Seeing that
aria-describedby="edit-user-cancel-method--description"whichedit-user-cancel-method--descriptiondoesn't exists.
Those 3 radio buttons don't have any description. So I think they don't need to have aria-describedby attribute.
For example, in REGISTRATION AND CANCELLATION > Who can register accounts?, the radio buttons don't have aria-describedby attribute since they don't have description.
However, the description below (Users with the Select method ...) is a description of the fieldset wrapper.

Proposed resolution
core/lib/Drupal/Core/Render/Element/Checkboxes.php and core/lib/Drupal/Core/Render/Element/Radios.php are responsible for copying the parent element's attributes to the children. Update both classes to remove the parent's aria-describedby from the copy before applying it to the children.
Remaining tasks
- Review
- Commit
User interface changes
Introduced terminology
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|---|---|---|
| #64 | Screenshot 2025-10-22 at 10.22.08 AM.png | 63.99 KB | smustgrave |
| #64 | Screenshot 2025-10-22 at 10.20.34 AM.png | 154.78 KB | smustgrave |
| #64 | Screenshot 2025-10-22 at 10.19.39 AM.png | 136.77 KB | smustgrave |
| Selection_003.png | 129.27 KB | Thew |
Issue fork drupal-2839344
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
mgiffordTested this in 8.3 & can repeat the problem. This is what the WAVE Toolbar sees:
Hard to argue with "the radio buttons don't have
aria-describedbyattribute since they don't have description."I wonder if it would make sense to open up a new issue for this relationship:
Agreed that they should be semantically linked, especially since that DIV isn't inside the fieldset.
Comment #8
lstrange commentedIs there any status on fixing this?
Thanks...
Comment #9
andrewmacpherson commented@lstrange - thanks for waking this issue up after 3 years :-)
The accessibility maintainers are aware of several other scenarios where an
aria-*ID reference wasn't working. Some issues have already been fixed, other issues have work-in-progress.This issue has a vague title (
aria-*ID refs in general), but a very detailed description of a problem with the registration/cancellation radio options on the user admin form.So for now, let's narrow the issue scope to the User module. Maybe it has a more general-purpose solution for all radio buttons; we'll see.
Comment #10
amanire commentedI'm guessing that this is a widespread problem in Drupal form widgets with containers since I'm also running into this on the Content Moderation module "Save As" select list widget. The form-wrapper div element has a an aria-describedby value of
edit-moderation-state-0--descriptionbut no corresponding ID exists on the form. This was confounding me since the description string "The moderation state of this piece of content." exists, but is never actually rendered on the node form.Comment #13
jontynewman commentedThis may be a naive approach, but may be useful as an initial patch.
By removing the attribute from radios, the description associated with the fieldset may be relied upon instead.
Comment #14
jontynewman commentedAn amendment to the previous patch.
Comment #15
jontynewman commentedComment #16
jacineThis is an issue with all radios and checkboxes elements (and maybe other elements), and has to do with how attributes are applied to these composite elements and their children.
What happens now is that:
1. The code that connects any
#descriptionpresent, witharia-describedbydoes not account for the fact that these elements are "special" composite elements with children.2. Attributes are simply pushed down to the children elements which results in the same attributes on BOTH the wrapper (fieldset) and the children (input type checkbox/radio).
This patch needs work because adding a
#descriptionto aradiosorcheckboxesand wanting to use it witharia-describedbyis perfectly fine. The patch just removes that functionality (for radios) entirely, preventing thearia-describedbyconnection from being made, instead of addressing the root issue, which IMO is that there should be a formal separation of attributes for composite form elements like this.Comment #17
jontynewman commentedThanks for the feedback, Janice.
I believe I would agree with your suggestions entirely if the form API currently provided some mechanism for associating specific
#descriptions with specific#options. Perhaps it would look something like this:However, perhaps this is beyond the scope of this issue, as this would seem to be feature enhancement of the form API, rather than a fix for the incorrect
aria-describedbyvalues.The patch does not remove any existing functionality. The
#descriptionwill still be generated with an ID which is associated with thearia-describedbyattribute of the wrapper (such as the fieldset that contains the checkboxes). The patch solely seeks to remove the erroneous attributes.I have amended the patch to also consider checkboxes, as you suggest.
Comment #19
jontynewman commentedThe testing now looks to have passed. It seems to have been a temporary issue in unrelated functional JavaScript tests.
Comment #20
jontynewman commentedI believe this issue to now be sufficiently re-scoped.
Comment #21
jontynewman commentedComment #22
jontynewman commentedComment #23
tanubansal commentedTested Patch #17 on 9.1, its appearing fine
This can be moved to RTBC
Comment #24
jontynewman commentedMany thanks, tanubansal.
Moving to RTBC as suggested.
Comment #25
andrewmacpherson commentedThis doesn't look very extensible. I'd expect this to be some kind of property/method on the class which defines the form element type, so that any element type might be a composite element. There are various places where this might be done, but I'm not sure which would be best.
Drupal\Core\Render\Element\ElementInterfaceorDrupal\Core\Render\Element\FormElementInterface. So then you might haveDrupal\Core\Render\Element\Checkboxes::isCompositeElement().CompositeFormElementInterface, so you can use ainstanceof CompositeFormElementInterfacetest.Comment #27
lauriiiBesides #25, this also needs tests.
Comment #29
codebymikey commentedThis issue also affects the Details form element, these accessibility issues are more apparent through the use of the Styleguide module.
https://www.drupal.org/project/styleguide
I think the details element would require a change to the twig template to include the appropriate wrapper and details attributes just like the fieldset template does. Or just simply omitted as well.
Comment #34
kalpaitch commentedFollowing on from @codebymikey's point #29 I've also identified this happens to the 'container' element too, (albeit with the caveat that the container element does not support the #description attribute):
This will result in a container wrapper being rendered with an incorrect 'aria-describedby' attribute.
Following on from @andrewmacpherson's comment #25, I agree there needs to be a more extensible way to declare a form element as a composite form element. But there are also needs to be some consideration for other render elements which get the 'aria-describedby' attribute added.
Comment #37
liam morlandI have made a merge request with patch 17 rebased onto 11.x.
Comment #38
solideogloria commentedAdded stack trace
Comment #39
solideogloria commentedFixed merge conflict.
Comment #40
solideogloria commentedI agree with #25, that we should use something more extensible. However, I don't think either of the suggestions is enough information for making it work. The issue is in the
FormBuilderclass, and at that point, you don't have instances ofElementInterface, you have a render array. So you can't do aninstanceoftest so easily.It might be easier to add an attribute
#is_compositeto the render array?Or if I'm wrong, then I would say it's probably better to have
CompositeFormElementInterface, because there is also aCompositeFormElementTrait. So you could even putisCompositein the trait, and it would be added to any contrib/custom classes using the trait as well.Comment #41
jldust commentedI agree that this needs to be more extensive, I'm seeing these broken references with selection options as well.
Comment #42
dcam commentedFor anyone who needs a workaround, you can implement this in a custom theme or module:
The same thing can be accomplished by adding a process function to individual checkbox and radio buttons. I tested it. Doing that means you don't need to loop through all the keys in the parent element to find its children. But my preference was to target the checkboxes and radios since they're the ones that have the problem.
Comment #45
dcam commentedI disagree with the approach in MR 7117 and from reading through comments it was contentious to begin with.
I spent the last couple of evenings studying the checkboxes and radios form elements, trying to figure out what parts of the system were setting wrappers and IDs and descriptions. I wanted to make absolutely certain that I'd found the source of these attributes so that it could be solved there.
It's the Checkboxes and Radios form element classes that do it. They both copy the parent element's attributes to the children, including the
aria-describedby. This choice is made unilaterally by those classes. The rest of the form system, e.g. FormBuilder, is just doing what it's supposed to do. The element classes are also managing the child descriptions or rather the lack of them. So if the element classes are setting the attributes, then they should be in charge of unsetting any irrelevant attributes. I opened MR 12287 with this strategy in mind. If this idea is accepted, then MR 7117 should be closed.I can sympathize with any desire to not duplicate code, but I will note that there is already a lot of duplicated code between the two elements. I probably wouldn't bother. If someone wants that to happen, then I assert that we should put off eliminating duplication for a follow-up.
Comment #46
dcam commentedI'll update the IS when a decision is made.
Comment #47
smustgrave commentedIf there are 2 MRs to choose from usually helps to still update the issue summary but in the proposed solution mention there are 2 solutions
Option 1 solves it by xyz
Option 2 solves it by abc
Comment #48
dcam commentedFair enough. It's done.
Comment #49
dcam commentedTweaked the Option 2 text.
Comment #50
dcam commentedComment #51
mgiffordThanks Liam. I'll see if I can get someone to renew it.
Comment #52
mgiffordComment #53
smustgrave commentednot a sub-maintainer but let me see if I can't help this along. Of the 2 options it seems like MR 7117 would be the most flexible correct? To add a new composite type I mean.
Comment #54
solideogloria commented@smustgrave What do you think about having an interface for composite elements? The check should probably be extensible, so that contrib modules can add composite elements and have them checked by extending the interface or having an attribute or something. See comment #40. Then you could check if the element extends the interface or if it has an attribute.
Comment #55
smustgrave commentedI can't speak as the form maintainer but if that made it easier to add a new item too I would be a +1
Comment #56
dcam commentedLet me be blunt: MR 7117 is a workaround for a problem caused by irresponsible copying of parent element attributes to the children. MR 12287 fixes the problem at its source. I feel strongly that it's the way to go, so much that I've considered unilaterally closing 7117 so this issue would progress. But I left it open to be fair to the others who have contributed here. By the way, 12287 has a test.
If there is interest in having the
isComposite()function or doing something similar with interfaces, then that can go into a follow-up issue as a feature request. Let's fix this bug properly instead.Comment #57
solideogloria commentedI do agree that MR 7117 is not the way to go. It's not flexible at all, as it won't work for anything added/changed by contrib.
This is a hardcoded list of types. Not flexible.
Comment #58
smustgrave commentedSo we want to close the 7117 then and go from there?
Comment #59
dcam commentedThat's what I would do.
Comment #60
smustgrave commentedAlright lets go that route
Rebase the MR
And if needed open a follow up
Comment #62
dcam commentedComment #63
dcam commentedRebased and updated the IS.
Comment #64
smustgrave commentedBefore
After
I also checked Require email verification when a visitor creates an account which should have aria-described
Ran the test-only job here https://git.drupalcode.org/issue/drupal-2839344/-/jobs/6981702 which shows the coverage
Believe this one is good to go.
Comment #67
catchCommitted/pushed to 11.x and cherry-picked to 11.3.x, thanks!
Comment #70
dcam commentedI can't wait to remove the workaround from my theme!