I'm trying to make a module that offers an #element_validate function which will check the element for dodgy looking data and if this is detected, tell the user this looks invalid and add a 2nd element to the form which can then be used to validate the dodgy-looking data.

The problem I'm having is that:

To add an element you need the form to rebuild. (see code referenced in the comment below)

To trigger a rebuild the form must have passed validation

This means that:

the #element_validate function cannot use form_error - else it would not be able to add the 2nd field. So I've had it instead set $form_state['rebuild'] = TRUE and issue a warning with drupal_set_message().

adding the 2nd element will only happen when there are not any other validation errors on the form (since these would also prevent the form rebuilding) leading to a confusing situation for the user: they will see the warning asking them to complete the 2nd element that doesn't exist. They'll correct the other errors on the form re-submit and then get that back again, this time with the 2nd element, which they'll have to complete before their submission is accepted.

Is there a way around this? e.g. a way to force a rebuild of the form from validation?

At line 968 in form.inc (Drupal 7.43), there is a condition ($form_state['rebuild'] || !$form_state['executed']) && !form_get_errors() before rebuilding the form. I am afraid there is no way to escape from this logic....
– Jimmy KoJul 1 '16 at 10:11

The validation function picks up the suspect value and stores a note that this form (uses #form_id) and element has a suspect value on a global variable. It also uses form_error() to inform the user of the problem.

The #pre_render function is called for the element. This function does not get the form object at all, which is why we specifically added #form_id to the element. Using this we can check the global variable for this form and see whether suspect data was found. It was, so add another form element in asking for confirmation. This element is not part of the Form API, so as well as the usual ('#type' => 'textfield' etc. we need to explicitly set '#name').

User confirms the data on the new form element and re-submits form.

The validation function notices the confirm data in $form_state['input'] (it won't be in values because this element is not part of the Form API's form) and compares it to the submitted value. It matches, so it does not call form_error().

Assuming no more errors, form submission continues.

I've also implemented a thing for storing the confirmed suspect value in a hidden field in case other fields fail verification, meaning the user only has to confirm the data once.

Use case

My specific use case is an email validator module. Beyond the drupal sytax check it can lookup DNS records and then conduct an SMTP conversation with a server to see if mail is accepted. Some of the SMTP errors can be temporary and so we use this chance to tell the user "hmmm. we got this error (user-friendly version of error goes here) - might be a temporary thing but can you double check your email?" allowing them to complete the form while still helping them avoid typos.