6

I have a Drupal form using ajax. When I submit the form I am getting the following being displayed:

enter image description here

When I check the Drupal log I have the following entry.

enter image description here

I have Googled the error and found this post but it applies to anonymous users. I am only using this with authenticated users.

Searching for the error in the code returns the ajax_get_form() function in ajax.inc

 $form_state = form_state_defaults();

  $form_build_id = $_POST['form_build_id'];

  // Get the form from the cache.
  $form = form_get_cache($form_build_id, $form_state);
  if (!$form) {
    // If $form cannot be loaded from the cache, the form_build_id in $_POST
    // must be invalid, which means that someone performed a POST request onto
    // system/ajax without actually viewing the concerned form in the browser.
    // This is likely a hacking attempt as it never happens under normal
    // circumstances, so we just do nothing.
    watchdog('ajax', 'Invalid form POST data.', array(), WATCHDOG_WARNING);
    drupal_exit();
  }

$form is not an object. I am not sure why.

In the ajax_get_form() function I am doing ado a dd() of $form_build_id. This returns me something like form-ymw-BN0Zhp9j2CZf82mZzslbUBiLo8kZbreXhB1y8kE.

If I perform the following SQL in the database I get a result.

select *  from cache_form where cid like '%form-ymw-BN0Zhp9j2CZf82mZzslbUBiLo8kZbreXhB1y8kE%';

The cid in this instance is form_state_form-ymw-BN0Zhp9j2CZf82mZzslbUBiLo8kZbreXhB1y8kE.

In form_get_cache() the code is as follows:

function form_get_cache($form_build_id, &$form_state) {
  if ($cached = cache_get('form_' . $form_build_id, 'cache_form')) {
     //code missing
  }
}

From cache_get() we are looking for form_form-ymw-BN0Zhp9j2CZf82mZzslbUBiLo8kZbreXhB1y8kEbut what we have stored in the database is form_state_form-ymw-BN0Zhp9j2CZf82mZzslbUBiLo8kZbreXhB1y8kE. Of course we do not match so nothing is returned.

I am unsure why this is happening.

My form code is as follows

  $form['welcome'] = array(
    '#markup' => //some html markup,
  );


  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Test'),
    '#ajax' => array(
      'callback' => 'myform_js',
      'wrapper' => 'message_container',
      'method' => 'replace',
      'effect' => 'fade',
      'event' => 'click',
    ),
  );

  $form['result'] = array(
    '#prefix' => '<div id="message_container">',
    '#suffix' => '</div>',
    '#type' => 'fieldset',
  );

The function myform_js() is not being hit as when dd() is added to the top it is not called. I can't see anything obviously wrong with this so not sure if there are any environmental issues that could cause the issue.

I have also tried changing the type of $form['submit'] to button.

It looks that the part that is failing is the call to cache_set(). I am unsure as why at the moment.

  • Please post your code that is generating your form, so we can try to help you. – michaelmol Mar 30 '15 at 8:56
  • @michaelmol form code has been added. – Rodney Mar 30 '15 at 9:13
  • global $conf is not where the form would be cached. The cache entry should be created in the cache_form table when you load the page. – Alfred Armstrong Mar 30 '15 at 10:28
  • @AlfredArmstrong question updated after looking in the correct cache location – Rodney Mar 30 '15 at 13:19
9

OK think I have it.

As form_form-ymw-BN0Zhp9j2CZf82mZzslbUBiLo8kZbreXhB1y8kE was not being set I looked at form_set_cache().

/**
 * Stores a form in the cache.
 */
function form_set_cache($form_build_id, $form, $form_state) {
  // 6 hours cache life time for forms should be plenty.
  $expire = 21600;

  // Cache form structure.
  if (isset($form)) {
    if ($GLOBALS['user']->uid) {
      $form['#cache_token'] = drupal_get_token();
    }
    cache_set('form_' . $form_build_id, $form, 'cache_form', REQUEST_TIME + $expire);
  }

  // Cache form state.
  if ($data = array_diff_key($form_state, array_flip(form_state_keys_no_cache()))) {
    cache_set('form_state_' . $form_build_id, $data, 'cache_form', REQUEST_TIME + $expire);
  }
}

I dug into cache_set()

function cache_set($cid, $data, $bin = 'cache', $expire = CACHE_PERMANENT) {
  return _cache_get_object($bin)->set($cid, $data, $expire);
}

I then looked into set()

  /**
   * Implements DrupalCacheInterface::set().
   */
  function set($cid, $data, $expire = CACHE_PERMANENT) {
    $fields = array(
      'serialized' => 0,
      'created' => REQUEST_TIME,
      'expire' => $expire,
    );
    if (!is_string($data)) {
      $fields['data'] = serialize($data);
      $fields['serialized'] = 1;
    }
    else {
      $fields['data'] = $data;
      $fields['serialized'] = 0;
    }

    try {
      db_merge($this->bin)
        ->key(array('cid' => $cid))
        ->fields($fields)
        ->execute();
    }
    catch (Exception $e) {
      // The database may not be available, so we'll ignore cache_set requests.
    }
  }

Here an exception is caught and not reported. I added a dd() into catch. Looking in drupal_debug.txt

[message:protected] => SQLSTATE[08S01]: Communication link failure: 1153 Got a packet bigger than 'max_allowed_packet' bytes

I have now increased the value of max_allowed_packet in MySQL. It has fixed things.

With this information I did some more googling and found this post. They have had the same problem that I had.

  • 3
    So much for empty catch clauses... Fantastic research! – znerol Mar 30 '15 at 20:08
2

After the "Invalid form POST data" watchdog message, notice that the ajax_get_form() function calls drupal_exit().

Your custom module can take advantage of this by hooking into hook_exit()... from there you can rebuild the expired/uncached form and attempt to resubmit form using Drupal's ajax_commands framework.

The following is an example of how I resolved this problem for an "AJAX-ified" user-login form.

/**
 * Implements hook_exit().
 */
function example_exit($destination = NULL) {
  if (arg(0) == 'system' && arg(1) == 'ajax') {
    $is_user_login_form_submission = isset($_POST) && isset($_POST['name']) && isset($_POST['pass']) && isset($_POST['form_build_id']);

    if ($is_user_login_form_submission) {
      watchdog(__FUNCTION__, 'User login AJAX form submission failed. Trying again...', array(), WATCHDOG_WARNING);

      $form_build_id = $_POST['form_build_id'];
      $form_state = form_state_defaults();
      $form_state['values'] = $_POST; // Important!
      $form = drupal_rebuild_form('user_login', $form_state);
      $form['#build_id_old'] = $form['#build_id']; // Important!

      // Try form submission again after it is rebuilt above
      $commands[] = ajax_command_update_build_id($form);
      $commands[] = ajax_command_invoke('form#user-login', 'trigger', array('submit'));

      print ajax_render($commands);
    }
  }
}
1

I know this question has been solved, but I encountered "Invalid form POST data" and the cause was different, so I'll throw it out there for people who come across this.

Basically, my culprit was a cron job that was especially zealous, clearing cache every 5 minutes. The issue wasn't with memory limits or writing to the database, but rather that fairly often the form would get cached, the cache would get cleared, and then attempting to get the form from cache would fail. Switching the cron job to hourly fixed it.

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.