hook_validate() returns errors, but the form has lost all the user's submitted values I'm sure there's a standard Drupal way of doing this, but I've tried a couple of the statements that I've found on blogs and stuff, and I can't get a form to restore the values submitted by the users.

For example:


form default value: "enter a long word"
user submits: "foo"
validation sets: "word not long enough"
form displayed on page with value: "enter a long word"
expected value: "foo"


So far, in the hook_nameform_validate function, I've tried setting form_state['rebuild'] and form_state['redirect'] to FALSE (I've seen both listed in blogs as the right answer).
Perhaps unsurprisingly to anyone who knows the correct answer, neither works individually, nor do they both work together.

EDIT as requested, here is some actual code to show what I'm doing (wrong).  You're correct that the question was probably too general to be of value without any actual code on display. This sample is still "abstracted" - there's closer to a dozen fields, but the effect is the same. 

In this case, with these functions (and the others, like theme etc...) when a user submits their email address, if the validation returns an error, the form tells them "bad user" then clears the email field (and all others).  Ideally the form should repopulate all fields with any typing they've already done.

function sampleform_nameform($form, &amp;$form_state) {
    $d = sampleform_defaults();
    $form = array(
        '#method'                   =&gt; 'post',
        '#prefix'                   =&gt; $d['form_prefix'].'&lt;div class="sample"&gt;',
        '#suffix'                   =&gt; '&lt;/div&gt;'.$d['form_suffix'],
        'your_email'                =&gt; array(
            '#value'            =&gt; $d['your_email'],
            '#type'             =&gt; 'textfield',
            '#field_suffix'         =&gt; '&lt;span class="required"&gt;*&lt;/span&gt;',
            '#required'             =&gt; TRUE,
            '#attributes'           =&gt; array('class' =&gt; array('standardInput'),
                                             'default' =&gt; $d['your_email'])
        ),
        'sampleform_submit'         =&gt; array(
            '#prefix'               =&gt; $d['clear'],
            '#type'                 =&gt; 'submit',
            '#value'                =&gt; $d['submit'],
            '#attributes'           =&gt; array('class' =&gt; array('buttonInput', 'submitInput')),
            '#executes_submit_callback' =&gt; true
        )
    );
    return $form;
}

function sampleform_nameform_validate($form, &amp;$form_state){
    $d = sampleform_defaults();
    if (!valid_email_address($form_state['input']['your_email'])) {
        form_set_error('your_email', $d['e_email_v']); 
    }
}


It's very possible that one of the two answers below has fixed the issue, and I've not had the chance to read them yet, so with a few more minutes, maybe we'l find the answer is already here.

:)

EDIT 2

kiamlaluno has shown us that while I was using #value in my form-builder function, the correct nondestructive setting in creation of a form is #default_value

If I look back to my original reference point, Pro Drupal 7 Development the form API examples in chapter 11 give the instruction to use #value on page 247 (where you build your first form).  

They also use MODULENAME_nameform as the hook, while it appears to be more correct to use MODULENAME_form (according to the way drupal works, it's easier to find further documentation if you search for _form rather than nameform).  

Anyways,  hopefully this saves someone else some time.  Someone should probably tell Todd Tomlinson and John K VanDyk that they've got an error in their book.

:)
There is nothing particular that needs to be done, to avoid to overwrite the value entered by the user, when a validation handler returns an error.

For example, the following form, is build from the code shown below (system_ip_blocking_form()):



function system_ip_blocking_form($form, $form_state, $default_ip) {
  $form['ip'] = array(
    '#title' =&gt; t('IP address'), 
    '#type' =&gt; 'textfield', 
    '#size' =&gt; 48, 
    '#maxlength' =&gt; 40, 
    '#default_value' =&gt; $default_ip, 
    '#description' =&gt; t('Enter a valid IP address.'),
  );
  $form['actions'] = array('#type' =&gt; 'actions');
  $form['actions']['submit'] = array(
    '#type' =&gt; 'submit', 
    '#value' =&gt; t('Add'),
  );
  $form['#submit'][] = 'system_ip_blocking_form_submit';
  $form['#validate'][] = 'system_ip_blocking_form_validate';
  return $form;
}


Its validation handler is the following one (system_ip_blocking_form_validate()):

function system_ip_blocking_form_validate($form, &amp;$form_state) {
  $ip = trim($form_state['values']['ip']);
  if (db_query("SELECT * FROM {blocked_ips} WHERE ip = :ip", array(':ip' =&gt; $ip))-&gt;fetchField()) {
    form_set_error('ip', t('This IP address is already blocked.'));
  }
  elseif ($ip == ip_address()) {
    form_set_error('ip', t('You may not block your own IP address.'));
  }
  elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) == FALSE) {
    form_set_error('ip', t('Enter a valid IP address.'));
  }
}


When I enter a value for which the validation handler returns an error, the value I enter is not modified, as you can see from the following screenshots.





This is what happens on Drupal 6 too. The only case where a field value is cleared is when the form validation handler reports an error for a password form field; in that case the password field doesn't contain the previously entered password, which would be difficult to edit as it is shown as "•••••."

There is nothing that needs to be set; by default Drupal will not change the content of a field when a validation handler reports an error for that field. As far as I know, the value of a field could be changed by its "#after_build" callback, that is invoked after the field is built, and that is described in form_builder() with the following sentence:


  The "#after_build" flag allows any piece of a form to be altered after normal input parsing has been completed.