diff --git includes/ajax.inc includes/ajax.inc
index 808cefb..8f935b6 100644
--- includes/ajax.inc
+++ includes/ajax.inc
@@ -253,9 +253,19 @@ function ajax_form_callback() {
     // Remove the value for form validation.
     unset($_REQUEST['ajax_triggering_element']);
   }
+  // To either validate or avoid validation we need to know whether this is
+  // a button or not. Grab the marker sent by js to see.
+  $ajax_triggered_by_button = TRUE;
+  if (!empty($_REQUEST['ajax_triggered_by_button'])) {
+    $ajax_triggered_by_button = ($_REQUEST['ajax_triggered_by_button'] == 'true');
+    // Remove the value for form validation.
+    unset($_REQUEST['ajax_triggered_by_button']);
+  }
+
   list($form, $form_state, $form_id, $form_build_id) = ajax_get_form();
 
-  // Build, validate and if possible, submit the form.
+  $form_state['ajax_triggered_by_button'] = $ajax_triggered_by_button;
+
   drupal_process_form($form_id, $form, $form_state);
 
   // This call recreates the form relying solely on the $form_state that
@@ -286,7 +296,7 @@ function ajax_form_callback() {
     $triggering_element = $form_state['clicked_button'];
   }
   // Now that we have the element, get a callback if there is one.
-  if (!empty($triggering_element)) {
+  if (!empty($triggering_element) && !empty($triggering_element['#ajax']['callback'])) {
     $callback = $triggering_element['#ajax']['callback'];
   }
   if (!empty($callback) && function_exists($callback)) {
@@ -328,7 +338,7 @@ function ajax_deliver($page_callback_result) {
   }
   elseif (is_array($page_callback_result) && isset($page_callback_result['#type']) && ($page_callback_result['#type'] == 'ajax_commands')) {
     // Complex AJAX callbacks can return a result that contains a specific
-    // set of commands to send to the browser. 
+    // set of commands to send to the browser.
     if (isset($page_callback_result['#ajax_commands'])) {
       $commands = $page_callback_result['#ajax_commands'];
     }
diff --git includes/form.inc includes/form.inc
index 41967f9..c812751 100644
--- includes/form.inc
+++ includes/form.inc
@@ -396,6 +396,7 @@ function form_state_keys_no_cache() {
   return array(
     // Public properties defined by form constructors and form handlers.
     'always_process',
+    'ajax_activated_element',
     'cache',
     'no_cache',
     'must_validate',
@@ -617,7 +618,7 @@ function drupal_process_form($form_id, &$form, &$form_state) {
     // browser don't increment all the element IDs needlessly.
     drupal_static_reset('drupal_html_id');
 
-    if ($form_state['submitted'] && !form_get_errors() && !$form_state['rebuild']) {
+    if ($form_state['ajax_triggered_by_button'] && $form_state['submitted'] && !form_get_errors() && !$form_state['rebuild']) {
       // Execute form submit handlers.
       form_execute_handlers('submit', $form, $form_state);
 
@@ -942,8 +943,18 @@ function _form_validate(&$elements, &$form_state, $form_id = NULL) {
     // #limit_validation_errors property is ignored if the button doesn't also
     // define its own submit handlers, because it's too large a security risk to
     // have any invalid user input when executing form-level submit handlers.
+    // form_set_error() is also suppressed in the case of a non-button AJAX
+    // activation of the form, in which case we want validation functions to
+    // succeed, but it's not the right time for errors.
+    $limit_validation_errors = FALSE;
     if (isset($form_state['clicked_button']['#limit_validation_errors']) && isset($form_state['clicked_button']['#submit'])) {
-      form_set_error(NULL, '', $form_state['clicked_button']['#limit_validation_errors']);
+      $limit_validation_errors = $form_state['clicked_button']['#limit_validation_errors'];
+    }
+    elseif (!empty($form_state['ajax_activated_element'])){
+      $limit_validation_errors = array();
+    }
+    if ($limit_validation_errors !== FALSE) {
+      form_set_error(NULL, '', $limit_validation_errors);
     }
     else {
       // As an extra security measure, explicitly turn off error suppression.
diff --git misc/ajax.js misc/ajax.js
index ee56aad..4149357 100644
--- misc/ajax.js
+++ misc/ajax.js
@@ -203,7 +203,7 @@ Drupal.ajax.prototype.beforeSubmit = function (form_values, element, options) {
   // Server-side code needs to know what element triggered the call, so it can
   // find the #ajax binding.
   form_values.push({ name: 'ajax_triggering_element', value: this.formPath });
-
+  form_values.push({ name: 'ajax_triggered_by_button', value: typeof(this.button) == 'object' });
   // Insert progressbar or throbber.
   if (this.progress.type == 'bar') {
     var progressBar = new Drupal.progressBar('ajax-progress-' + this.element.id, eval(this.progress.update_callback), this.progress.method, eval(this.progress.error_callback));
