Steps to reproduce:

  • Add at least 2 file fields to a webform
  • Enable page caching for anonymous users and set expiration of cached pages to, eg. 1 hour
  • View webform as anonymous user and ensure the webform is being served from Drupal cache by checking the response headers (ie. X-Drupal-Cache: HIT
  • Add a file to the first file field. Click upload. File should upload correctly.
  • Add a file to the second file field. Click upload. An error will arise reading: An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (50 MB) that this server supports.

After looking at the code, the issue appears to be that if the form is served from the cache, then upon the first file submission the form_build_id is updated (to avoid other anonymous users sharing the same form_state).

Upon attempting to upload the second file, the form successfully sends the updated form_build_id, but Drupal/webform appears to still be expecting the old form_build_id and in line ~241 of module/file/file.module the form submission is rejected.

I have confirmed that this issue doesn't occur with, eg. multiple file uploads on a public node form — these correctly update the form_build_id on both client and server.

Comments

drcolossos’s picture

Priority: Major » Critical

I can confirm this issue as well. It is reproduceable in all my test scenarios as described above. The anonymous users share a form_id. On the first AJAX call, a Session cookie gets set, on the second AJAX call, the form_id gets changed during the call since a session has been opened. Therefor, the check that triggers the mentioned error (post'ed form_id vs. actual form_id) fails.

On a complete page reload, everything works as expected since the Session cookie keeps the form_id constant.

danchadwick’s picture

Priority: Critical » Normal

Please review the issue queue handbook for priorities. Patches welcome.

danchadwick’s picture

Maybe just hide the upload button as a work-around? See https://www.drupal.org/node/2374203#comment-9355357

mfaraji’s picture

Hi Dan,

I have the same issue with both webforms and our own form created using Form API`s managed_file. Your recommendation is to hide the upload button, how to do it? by setting properties in css or there is properties in form api array for the field to hide the upload button, I searched but could not find any thing in this regard. Or what else can I do to not get such a error in first place.

danchadwick’s picture

You can hide the upload button with css or with a #process function, perhaps by replacing the core #process function, calling it, and then removing the button. I just googled to find these; I haven't tried any of them.

drcolossos’s picture

It's not an issue with the upload button but the way Drual generates/caches the form build id. I found 2 issues on core that are related to that. Its basically a core issue with the way the whole form is cached (including the form build id) and delivered. When submitting more than one file, the session information is up-to-date, but the cached form token isn't.

danchadwick’s picture

Re #6 -- it would seem that removing the upload button would circumvent the core bug, no? If so, it would seem to be a reasonable solution since getting 7 core patched is probably well neigh impossible. If so, then should it be hidden with CSS, which would be easy to reverse for themers or removed from the process function, which would not?

drcolossos’s picture

Ah ok, I see, you mean this will stop the AJAX call and just uses the form in a "non-AJAX" way? I will test this and come back with the results.

mikemccaffrey’s picture

It took me more time than I care to admit to track down that this was my problem. I had assumed that varnish caching was interfering with the form for anonymous users, when it was actually drupal caching and this weird issue.

Anyway, it seems that when the first file is uploaded, a command is returned in the resulting JSON for the core ajax.js script to update the form build id value. It looks something like:

{"command":"updateBuildId","old":"form-LwJP3GmMY9psNxXskMz2fMSS1mxdfcrIvXzO6LXTjBY","new":"form-65D0hAbgho9uGNrHWaKp98Mmxqkud1y0JVi9NFo421c"}

It looks like the form_build_id input for the form is being properly changed to the new value in the updateBuildId command, and can be properly posted to the server on the main form submit.

However, it seems that there is an Drupal.ajax object defined for each of the upload buttons, and the ajax.options.url property for each remains the same even after the main form_build_id has been updated. So when the eventResponse function is triggered on button click, the ajax request is sent to the original url with the old id appended to the end, with the new id in the request itself. Then the mismatch triggers the error.

What is the best way to address this? Should the updateBuildId function iterate through all of the Drupal.ajax objects and change the url? Or should something be added to file.js to re-bind ajax objects to the buttons when the form is modified?

(Update: replacing the token in the Drupal.ajax[i].url object does not change the destination of the ajax calls. The url may already be baked into the attached function somehow)

danchadwick’s picture

Mike - I'd hide the upload buttons, although this has not been reported has having been successfully tested.

quicksketch’s picture

I'm not sure what to do here either. I took a stab at this entire problem several months ago in #343415-21: Form cache is not cleared on submit when page cache is activated. One thing I can definitely say is you shouldn't ever set your page cache above 6 hours, otherwise you'll run into a whole new bucket of problems.

The updateBuildId method is relatively new, within the last 6 months of Drupal 7 versions. Prior to that, this problem may not have existed; I can't say for sure.

Because this sounds like a core issue, as far as Webform is concerned we'll probably just be talking about work-arounds. You might try something so simple (but potentially worrisome) as disabling the page cache on your form if feasible.

Or should something be added to file.js to re-bind ajax objects to the buttons when the form is modified?

Because doing a file upload replaces the entire contents of the file field already, it should already be re-binding any AJAX objects to the buttons, because they're entirely replaced. If that's not already happening, that might be the place to look.

mikemccaffrey’s picture

Because doing a file upload replaces the entire contents of the file field already, it should already be re-binding any AJAX objects to the buttons, because they're entirely replaced. If that's not already happening, that might be the place to look.

The file upload does replace the entire contents of the field whose button was submitted, so that will actually still work if you submit it again. It is any other field upload fields that you have which will start returning errors if you try submitting them.

Because this sounds like a core issue, as far as Webform is concerned we'll probably just be talking about work-arounds.

Yeah, we might want to close this issue, and create another for core. There are plenty of work-arounds, but the bottom-line right now is that there is no way to cache a webform for anonymous users if you have more than one field making ajax calls.

The updateBuildId method is relatively new, within the last 6 months of Drupal 7 versions

Do you have any idea when/why exactly it was added? I can't seem to find the original issue easily.

David_Rothstein’s picture

Do you have any idea when/why exactly it was added? I can't seem to find the original issue easily.

The updateBuildId method was added as part of the Drupal 7.27 security release (https://www.drupal.org/drupal-7.27-release-notes) which is why you can't find the original issue.

I created a core issue for this now: #2392117: Forms with multiple file form elements produce upload errors with the Drupal page cache enabled

I thought maybe we can just remove the code from the File module that produces this error, since it's not really clear to me what purpose it actually serves?... but perhaps it's not that simple. The patch I posted there does fix the bug though.

andyanderso’s picture

Same issue for me. I used the patch referenced in #13 to fix my issue in D7. Seems like David_Rothstein is right and the error message is redundant/not-needed anyway....

danchadwick’s picture

Status: Active » Closed (won't fix)

Since this is a core issue, I don't see much purpose in keeping this open here. The solution for anyone stumbling upon this would be to hide the upload button with CSS.

pontus_nilsson’s picture

Adding this for other people who had this problem. Here's a way to remove the upload button for all anonymous users.

/**
 * Implements template_preprocess_webform_managed_file().
 */
function MYTHEME_preprocess_webform_managed_file(&$vars) {
  if (user_is_anonymous()) {
    $vars['element']['upload_button']['#access'] = FALSE;
  }
}
zietbukuel’s picture

If anyone's having this issue you can use this patch to fix it:
https://www.drupal.org/project/drupal/issues/2392117#comment-12616590

kris77’s picture

Solution posto in #16 works for me.

Thanks @pontus_nilsson