Solving the ‘An illegal choice has been detected. Please contact the site administrator.’ error
After upgrading a couple of client websites to Drupal 6.13, we suddenly noticed several “An illegal choice has been detected. Please contact the site administrator.” errors. These errors popped up all around: Performance settings, User adding, u name it.
Research thought me that the illegal choice error was returned when the posted! value of a checkbox, select or radio form field was not available in the original form-structure array. It all sounds logical as an valid input filter, because you don’t want people posting illegal values in your forms now do you?
I found out that the code generating the radio buttons was the following:
We were able to trace it back to the following piece of code in /includes/form.inc line 1733
1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 | /** * Roll out a single radios element to a list of radios, * using the options array as index. */ function expand_radios($element) { if (count($element['#options']) > 0) { foreach ($element['#options'] as $key => $choice) { if (!isset($element[$key])) { // Generate the parents as the autogenerator does, so we will have a // unique id for each radio button. $parents_for_id = array_merge($element['#parents'], array($key)); $element[$key] = array( '#type' => 'radio', '#title' => $choice, '#return_value' => check_plain($key), '#default_value' => isset($element['#default_value']) ? $element['#default_value'] : NULL, '#attributes' => $element['#attributes'], '#parents' => $element['#parents'], '#id' => form_clean_id('edit-'. implode('-', $parents_for_id)), '#ahah' => isset($element['#ahah']) ? $element['#ahah'] : NULL, ); } } } return $element; } |
and more specifically this line (1747):
1747 | '#return_value' => check_plain($key), |
Tracking down further learns us the source of all evil hides in bootstrap.inc line 728!
728 729 730 731 732 733 734 735 736 | /** * Encode special characters in a plain-text string for display as HTML. * * Uses drupal_validate_utf8 to prevent cross site scripting attacks on * Internet Explorer 6. */ function check_plain($text) { return drupal_validate_utf8($text) ? htmlspecialchars($text, ENT_QUOTES) : ''; } |
This little piece checks if the passed argument is a valid UTF8 string, and if so htmlspecialchars it, or if not, returns nothing! This causes the return values of our radio button items to have an empty value=”" attribute, causing the validation to fail because of the empty value you posted.
Change it to this code to get it back up on it’s feet:
728 729 730 731 732 733 734 735 736 | /** * Encode special characters in a plain-text string for display as HTML. * * Uses drupal_validate_utf8 to prevent cross site scripting attacks on * Internet Explorer 6. */ function check_plain($text) { return drupal_validate_utf8($text) ? htmlspecialchars($text, ENT_QUOTES) : $text; } |
Hope to be of any service!
This change is in fact removal of security measures (instead of rejecting broken utf8 for security reasons, we now return it, even skipping safety-escaping!) We might better think about WHY check_plain was used on checkboxes.
Hi JirkaRybka,
I completely agree with you on the security matter, but the problem is situated in the fact that when running numerical values like 0 or 1 through check_plain, the returned value was empty.
This returns in empty radio-buttons and thus “illegal choices”.
Greets
Dear author,
I am afraid I was not able to solve my problems with your suggestions. While browsing through the form.inc file, I noticed that the problem lies _form_validate function (Which is used if anyone intends to use the built in form validation techniques implemented in the Forms API.)
In that, it specifically states its function as:
* Performs validation on form elements. First ensures required fields are
* completed, #maxlength is not exceeded, and selected options were in the
* list of options given to the user. Then calls user-defined validators.
*
Hello Author,
I am facing same problem when i am populating data into selectbox through ajax when user select any value from it’s parent select box.I tried the above trick but it seems not working for me.
I search lot nothing appears to be working for me.
Please help
Hi Nehal,
This issue originates from the fact that a value was posted, which was not in the original form-setup array, and thus forms an illegal choice
when matching the posted values against the proposed values in the form-array.
Populating your (connected) select boxes through ajax is a good technique, although you should have a look at the following implementations:
I hope I have put you on the right track, otherwise come back for more!
Kind regards,
Kim