Drupal - How to avoid "An illegal choice has been detected" when updating select list values via ajax?

--- Original answer. Is wrong, check update below.

Just set

$bla['#validated'] = TRUE

On the select that will be updated with AJAX.

Checkout "Illegal Choice has been detected"

--- Update 17 Dec 2019 ---

Actually that was not good advice, that was a long time ago, now I know better.

Ajax callback function is supposed to only return the piece of form that has changed or some ajax commands.

In this case form modification must be made on the form function or a form alter hook. Form building and validation functions are run each time an ajax callback is called. Just check $form_state on your form build or alter function and adjust values accordingly.

See Resolve the error "An illegal choice has been detected..."

I have faced this problem number of times in Drupal 6, changing the values of Select list via Ajax.

Here is what you can do is

  1. If possible, try to insert all the possible values in the in the Field API, you need to edit that field, and then when you change the values, you will have to make sure that only the listed values are in the list.
  2. Solution that I implemented was, you can change the field type from Select list to textfield, from Field API, and ask Drupal to save the value as is. Now, on form runtime, you will have to alter the form, change the type to select list, and push in options, which you want to. While submission as well, you will have to assign the correct value required by the field, that will be saved in DB as is. Make sure your submit handler is called before the default form submit handler, for that you may use array_merge

If first solution works for you, you will save a lot of time coding and testing things. BUT if the list in the drop down is dynamic and you have no control over it, you might have to go with second solution.

Do post if you face any issues, I have done quite lot of work on this problem, for my project, and I did for ~50 forms, PHEW !!! :)

I have found the best way round this is to set the #value on the select list.

If we have the value in formstate and it is in our options then we will not get an error. If our value is not in the options then select the first option in our options and use that.

$form['example'] = array(
    '#type' => 'select',
    '#title' => t('Example'),
    '#options' => $options,
    '#value' => isset($form_state['values']['example']) && in_array($form_state['values']['example'],$options)?$form_state['values']['example']:key($options),