I am using Drupal Commerce 2 and have created a CheckoutPaneBase for shipping methods that we get from an internal service. I am not using Ajax and when the user adds their shipping address, they go to the ShippingServicePane.

in the buildPaneForm, I call out to the service and then create an array of options that appear in a radio button input. They show correctly but when I hit continue to go to the preview pane, it gives me a 'An illegal choice has been detected. Please contact the site administrator.' All the issues I am finding online seem to relate to an Ajax call, which my site is not using, or a missing value, which my options are not missing.

Here is my buildPaneForm

  public function buildPaneForm(array $pane_form, FormStateInterface $form_state, array &$complete_form) {
    $service= new ShippingServiceRepository();
    $options = [];
    $shipping_methods = $service->get_shipping_methods();
    $i = 0;
    foreach ($shipping_methods as $shipping_object) {
        $options[$i] = $this->t($shipping_object->Name.' - '.str_replace('USD ','',$shipping_object->TotalCharge));

    $pane_form['shipping_service'] = [
      '#type' => 'radios',
      '#default_value' => 0,
      '#options' => $options,
      '#required' => FALSE,
    return $pane_form;

which generates a radio field like this:

<div class="js-form-item form-item js-form-type-radio form-type-radio js-form-item-shipping-services-custom-pane-shipping-service form-item-shipping-services-custom-pane-shipping-service">
        <input data-drupal-selector="edit--shipping-services-custom-pane-shipping-service-0" type="radio" id="edit-shipping-services-custom-pane-shipping-service-0" name="shipping_services_custom_pane[shipping_service]" value="0" checked="checked" class="form-radio">
        <label for="edit-shipping-services-custom-pane-shipping-service-0" class="option">UPS Ground - $12.06</label>

I saw and tried adding '#required' => FALSE but it also didn't solve the issue.

When I hardcoded the options, it works fine:

$options = [
  0 => $this->t('Free Shipping $0'),
  1 => $this->t('Next Day $15')
  • Add $config['system.logging']['error_level'] = 'verbose'; to settings.php to see the actual error. OR check the drupal log /admin/reports/dblog – No Sssweat Dec 1 '20 at 19:49
  • I enabled the verbose logging and I am getting an 'Illegal choice 1 in shipping_services_custom_pane element.' but I don't understand why that would be – Wally Kolcz Dec 1 '20 at 19:58
  • Is it because it is a multipart form and Drupal is not happy that I am building the form dynamically before the step is shown? – Wally Kolcz Dec 1 '20 at 20:02
  • I added '#validated' => TRUE and it works now, but is that the correct way? – Wally Kolcz Dec 1 '20 at 20:04
  • I'm not that familiar with Commerce, but I think that you might need to implement either a #process or #pre_render callback in order to inject options. The error you are receiving essentially means that the form, as presented, is not consistent with the form as stored in cache. Setting '#validated' => TRUE is just tricking the form into skipping validation for that field, which will create the appearance of a functioning form while masking a deeper error. The first thing that I think you should check is whether or not buildPaneForm is, in fact, being called for every step. – Beau Dec 7 '20 at 21:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.