Home > Drupal, PHP > Solving the ‘An illegal choice has been detected. Please contact the site administrator.’ error

Solving the ‘An illegal choice has been detected. Please contact the site administrator.’ error

August 13th, 2009

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!

Author: Kim Categories: Drupal, PHP Tags:
  1. JirkaRybka
    August 13th, 2009 at 23:13 | #1

    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.

  2. Kim
    August 14th, 2009 at 08:14 | #2

    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

  3. abebe
    September 18th, 2009 at 13:06 | #3

    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.
    *

  4. Nehal Rupani
    January 20th, 2010 at 21:11 | #4

    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

  5. Kim
    January 21st, 2010 at 10:28 | #5

    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

  1. No trackbacks yet.