How do I make form_set_error() affect the submitted form only? I have a couple of different forms on one page; each has a 'name' field.

Sensibly, on submission drupal processes the submitted form, but in what seems a completely barmy way of doing things, form_set_error() just takes a field name, and appears to have no knowledge of which form was submitted.

So if the name field is invalid on the submitted form, it gets marked as invalid on all the other forms that use a field called 'name'.

Does this mean that every field needs a unique field name, e.g. name-somemodule1 and name-somemod2? This will break user convenience (browser probably knows what to pre-fill for 'name').

Sure I've missed something, but have read a lot of FAPI pages and code comments and am still confused.

In the screenshot below, you'll see 2 forms. The one on the left is the submitted one, with 'invalid-name' in a field called 'name'. The one on the right is not submitted (can only physically click one submit button at once!) and yet it's 'name' field (with 'admin' in it) is also marked invalid.

These are entirely separate forms.



Here's my modules' code (email field omitted, some attrs omitted just to reduce post size)

function mymod_contact_form() {
  $form['comment'] = array(
    '#title'         =&gt; t('Enter query'),
    '#default_value' =&gt; '',
    '#type'          =&gt; 'textarea',
  );                                                             

  $form['name'] = array(
    '#title'         =&gt; t('Enter name'),
    '#default_value' =&gt; '',
    '#type'          =&gt; 'textfield',
  );

  $form['submit'] = array(
    '#type'  =&gt; 'submit',
    '#value' =&gt; 'Send',
  );

  return $form;
}

function mymod_contact_form_validate($form, &amp;$form_state) {
  $name = trim($form_state['values']['name']); 
  if (strpos($name,' ') === FALSE) {
    form_set_error('name', t('Please enter your full name'));
  }
}

function mymod_contact_form_submit($form, &amp;$form_state) {
  $vals = $form_state['values'];
  // …
}
There is definitely a limitation in Drupal core that is causing form fields with with the same name to be styled as "error" even through the validation error happened on a different form that was submitted on the same page.  Looking at the code for Drupal version 6,7,8 (as of now) - they all have this limitation.  The good news is that the patch I put together (for Drupal 6) to fix this problem is fairly minimal and it involves using the form_id to properly scope the form element with the validation error.  I've haven't done much testing with it, so use it at your own risk.  The changes required for Drupal 7 should be similar.

diff --git a/includes/form.inc b/includes/form.inc
index 1529c19..4052133 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -798,29 +798,41 @@ function form_execute_handlers($type, &amp;$form, &amp;$form_state) {
  *   The error message to present to the user.
  * @param $reset
  *   Reset the form errors static cache.
+ * @param $scoped
+ *   Returns the errors scoped with the form_id
  * @return
  *   Never use the return value of this function, use form_get_errors and
  *   form_get_error instead.
  */
-function form_set_error($name = NULL, $message = '', $reset = FALSE) {
+function form_set_error($name = NULL, $message = '', $reset = FALSE,$scoped = FALSE) {
   static $form = array();
+  static $form_scoped = array();
   if ($reset) {
     $form = array();
+    $form_scoped = array();
   }
   if (isset($name) &amp;&amp; !isset($form[$name])) {
     $form[$name] = $message;
+    if(isset($_POST['form_id'])) {
+      $name = $_POST['form_id'] . "::" . $name; // Scope it based on form_id
+    } 
+    $form_scoped[$name] = $message;   
     if ($message) {
       drupal_set_message($message, 'error');
     }
   }
-  return $form;
+  if($scoped) return $form_scoped;
+  else return $form;
 }

 /**
  * Return an associative array of all errors.
+ * 
+ * * @param $scoped
+ *   Returns the errors scoped with the form_id
  */
-function form_get_errors() {
-  $form = form_set_error();
+function form_get_errors($scoped = FALSE) {
+  $form = form_set_error(NULL,'',FALSE,$scoped);
   if (!empty($form)) {
     return $form;
   }
@@ -828,16 +840,29 @@ function form_get_errors() {

 /**
  * Return the error message filed against the form with the specified name.
- */
-function form_get_error($element) {
-  $form = form_set_error();
-  $key = $element['#parents'][0];
-  if (isset($form[$key])) {
-    return $form[$key];
-  }
-  $key = implode('][', $element['#parents']);
-  if (isset($form[$key])) {
-    return $form[$key];
+ * 
+ * * @param $scoped
+ *   Returns the error scoped with the form_id
+ */
+function form_get_error($element,$scoped = FALSE) {
+  if($scoped &amp;&amp; isset($element['#form_id'])) {
+    $form_id = $element['#form_id']; // Try to make the error form specific
+  } 
+  $form = form_set_error(NULL,'',FALSE,$scoped);
+    
+  for($i=0;$i&lt;2;$i++) {
+    if($i==0) $key = $element['#parents'][0];
+    else $key = implode('][', $element['#parents']);
+     
+    if(isset($form_id)) {
+      $key0 = "$form_id::$key"; // Provide the scope based on form_id
+      if(isset($form[$key0])) {
+        return $form[$key0];
+      }
+    } 
+    if (isset($form[$key])) {
+      return $form[$key]; // If form_id not found then try without it
+    }
   }
 }

@@ -896,6 +921,7 @@ function form_builder($form_id, $form, &amp;$form_state) {
   $count = 0;
   foreach (element_children($form) as $key) {
     $form[$key]['#post'] = $form['#post'];
+    $form[$key]['#form_id'] = $form_id;
     $form[$key]['#programmed'] = $form['#programmed'];
     // Don't squash an existing tree value.
     if (!isset($form[$key]['#tree'])) {
@@ -2246,7 +2272,7 @@ function _form_set_class(&amp;$element, $class = array()) {
   if ($element['#required']) {
     $class[] = 'required';
   }
-  if (form_get_error($element)) {
+  if (form_get_error($element,TRUE)) {
     $class[] = 'error';
   }
   if (isset($element['#attributes']['class'])) {