Drupal 7 "The form has become outdated. Copy any unsaved work in the form below and then reload this page." I have a problem. When I put this code in my module checkboxes work great but form throws an error:

"The form has become outdated. Copy any unsaved work in the form below and then reload this page."


Alert one add node form.

function automobil_form_auto_node_form_alter(&amp;$form, &amp;$form_state, $form_id) {

  $form['add_phone'] = array (
    '#type' =&gt; 'fieldset',
    '#title' =&gt; t('Adauga telefon'),
    '#weight' =&gt; 6,
    '#collapsible' =&gt; TRUE,
    '#collapsed' =&gt; TRUE,
  );

  $form['add_phone']['phone'] = array(
    '#type' =&gt; 'phone',
    '#title' =&gt; t('Phone Number'),
    '#required' =&gt; TRUE,
  );

  $form['add_phone']['phone_submit'] = array (
    '#type' =&gt; 'button',
    '#value' =&gt; 'Add',
    '#prefix' =&gt; '&lt;div class="col-md-6 text-left"&gt;',
    '#suffix' =&gt; '&lt;/div&gt;',
    '#ajax' =&gt; array(
      'callback' =&gt; 'automobil_add_phone_callback',
      'wrapper' =&gt; 'add-new-phone',
      'effect' =&gt; 'fade',
    ),
  );

  if(isset($form_state['values']['phone']['prefix'])) {
    $upload_users_phone = _upload_users_phone($form, $form_state, 'add');
  } else {
    $upload_users_phone = _upload_users_phone($form, $form_state, '');
  }

  $form['field_temporary_phone'] = array(
    '#prefix' =&gt; '&lt;div id="add-new-phone"&gt;',
    '#suffix' =&gt; '&lt;/div&gt;',
    '#title' =&gt; t('Telefon'),
    '#type' =&gt; 'checkboxes',
    '#weight' =&gt; 5,
    '#options' =&gt; $upload_users_phone,
  );
}


Ajax Callback

function automobil_add_phone_callback(&amp;$form, &amp;$form_state) {
  return $form;
}


Function that returns a list of user_phone that is stored in user profile.
If a user adds new phone it adds it and return the updated list.

    function _upload_users_phone($form, $form_state, $operation, $returns) {
  global $user;
  // load user

  $existingUser = user_load($user-&gt;uid);

  if($operation == 'add') {
    $existingUser-&gt;field_user_phone['und'][]['value'] = $form_state['values']['phone']['prefix'] . ' ' . $form_state['values']['phone']['number'];
    $user = user_save((object) array('uid'=&gt;$user-&gt;uid), (array) $existingUser);
  }
    // edit field-data
  foreach ($existingUser-&gt;field_user_phone['und'] as $key =&gt; $value) {
    $val_list[] = $value['value'];
    $key_list[] = $key;
  }

  return $val_list;
}


What is a problem (why is the 'form outdated form' error thrown)?
The reason you get the "The form has become outdated." error is because the token fails validation (meaning the old string doesn't match the new string). The token is determined by running a hash on several things including the session_id(). When you call user_save(), the session is changed/invalidated/whatever. Hence, when the token is tested next time around, it generates a different token because you have a different session_id() and therefore fails, thinking you were using an old form.

I have the same issue after calling user_save() from within my form processing. The way I fixed it feels like a pretty big hack, but I don't know that there's a clean way around it. The form code doesn't seem to expect that you would ever change/invalidate the session whilst in the middle of handling a form (in this case, a multi-part form).

Once you call user_save you must modify the form state manually to insert the updated token. Then, when it checks tokens your new token will match since you generated it after the 'session_id()' was updated.

So the resulting code looks like this. Notice that the input element is changed for $form_state to pass by reference so that the modification gets propagated up.

function _upload_users_phone($form, &amp;$form_state, $operation, $returns) {
  global $user;
  // load user

  $existingUser = user_load($user-&gt;uid);

  if($operation == 'add') {
    $existingUser-&gt;field_user_phone['und'][]['value'] = $form_state['values']['phone']['prefix'] . ' ' . $form_state['values']['phone']['number'];
    $user = user_save((object) array('uid'=&gt;$user-&gt;uid), (array) $existingUser);
    $form_state['input']['form_token'] = drupal_get_token($form['#token']);
  }
    // edit field-data
  foreach ($existingUser-&gt;field_user_phone['und'] as $key =&gt; $value) {
    $val_list[] = $value['value'];
    $key_list[] = $key;
  }

  return $val_list;
}


You might also need to set $form_state['values']['form_token'], but I'm pretty sure it's unnecessary. Again, don't forget the &amp; in front of the declaration of the function variable $form_state or the value won't make it into the real array.