Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTMX Form multipart fails to upload file in production - Empty request - Both [2.0.3] and [1.9.3] !! #3008

Open
LordPraslea opened this issue Nov 8, 2024 · 2 comments

Comments

@LordPraslea
Copy link

When trying to upload a file via HTMX with multipart, it fails to do anything and just posts an empty request without any object/request information. (Empty request)

I've been trying to debug this and cannot seem to find any reason why HTMX fails to upload or send any object data for this particular field.
The file I'm trying to upload is between 40 and 60 MB.

On a local testing environment everything works flawlessly, same code/binary gets deployed to the webserver and then upload fails

Same issue occurs in both versions of HTMX "1.9.3" and upgraded to "2.0.3" seems to be the same thing.

I've been using firefox 115 and librewolf 131.

I should mention that I added hx-post and hx-encoding while debugging, previously, only the action field existed, and a hx-boost exists somewhere at top level.

I added the progress and on localhost it seems to function properly, on production it does something but then quickly jumps to full and a 400 HTTP error gets returned by the server.

htmx-failure-upload

Example code

	<form id="upload-form" action={ templ.SafeURL(fmt.Sprintf(`/uploads/new`)) }  hx-post={ fmt.Sprintf(`/uploads/new`) }  method="POST" class="form signup"
			enctype="multipart/form-data"  hx-encoding="multipart/form-data" hx-target="#uploads-modal-inner">
		for _, err := range form.NonFieldErrors {
      <div class="notification is-danger is-light">{err}</div>
		}
			<!-- Include the CSRF token -->
			<input type="hidden" name="csrf_token" value={data.CSRFToken}> 
   
			if !form.New  {
		  	<input type="hidden" name="id" value={ c.Tostr(form.ID) }>
        <input type="hidden" name="uuid" value={ form.UUID.String() }>
			}
		
..... description  input field & misc 
... file_type select field & misc..

    <div class="field">
      <div class="file is-info has-name">
        <label class="file-label">
   
          <input placeholder="File" type="file" name="file" 
            class={ "file-input", templ.KV("is-danger", form.FieldErrors["File"]) }   />

          <span class="file-cta">
            <span class="file-icon">
              <i class="fas fa-upload"></i>
            </span>
            <span class="file-label"> Select File to Upload </span>
          </span>
          // if form.File != "" {
            <span class="file-name">   { form.File } </span>
          // }
        </label>
      </div>


    </div>	
    


    <progress id="progress" value="0" max="100"></progress>
    <div class="field is-grouped">
      <div class="control">
			<input type="submit" class="button is-primary" if form.New {         value="Upload File"          } else {          value="Edit uploaded file"     }  >
      </div>
    </div>
    </form>
    <script>
        htmx.on('#upload-form', 'htmx:xhr:progress', function(evt) {
          htmx.find('#progress').setAttribute('value', evt.detail.loaded/evt.detail.total * 100)
        });
    </script>
		
		
@Telroshan
Copy link
Collaborator

Hey, sounds weird, especially as you say it works fine locally, and as the Content-Length header seems to be correct (~40MB).
Some ideas out loud:

  • Does it work with a smaller file?
  • Do you have any logs in your backend, either your app itself or the apache/nginx/whatever else you might be using in front of it?
    (for a very specific example, it might be a apache/nginx config that doesn't let your request live long enough to transfer the whole file, constraint that you might not have in a local environment)
  • When is that 400 error fired exactly in your backend ? If that's within your app, can you add debug breakpoints and see what's going wrong at that spot?

It sounds like a backend issue to me, as htmx has no reason to work differently between your 2 environments here.
Hope this helps you investigate!

@LordPraslea
Copy link
Author

LordPraslea commented Nov 9, 2024

I reviewed tens of existing and past HTMX bug reports, added, removed various configs
So I went in a debugging investigation frenzy and ended up even adding hx-headers for CORS, so I could go further on the backend and investigate further (although CORS WAS already in the input but nothing was sent via HTMX). Logs on the backend where as if they did NOT reach the current function. This is what I found weird and made me think of an HTMX problem.

If a upload is lower than 5MB, it gets sent through.. If it's larger (say 20~50 mb) it fails. There is an empty request sent. On multiple browsers i had the same thing.

I then set a timeout in HTMX with hx-request for 1000 seconds.. and HTMX eventually gave an error which cancelled the upload giving a server context error. (

HTMX error

SyntaxError: JSON.parse: expected double-quoted property name at line 1 column 20 of the JSON data
    parseJSON /static/js/htmx.js:804
    getValuesForElement static/js/htmx.js:3792
    issueAjaxRequest https://mastery.linsublim.com/static/js/htmx.js:4302
  .... 30more lines
htmx.js:2963:15

Upon closer examination in the proxy config nothing seemed to time out, this lead me to investigate the backend and it does seem that there was a timeout setting somewhere. It was not clear to me if it was a readheader timeout or a simple read timeout so when I increased it to a certain point it seemed to work for certain uploads.

It might be useful for HTMX to give an error if it can't upload, gets no response or gets a non 200 response especially when uploading. Because i'm thinking of the many cases when intermediary proxies CAN block. Luckily for me, caddy has sane defaults and no timeout exists and it was easier to debug.

UPDATE: IT seems that even with updated timeouts, it still fails to upload and still sends the empty request so I still need to review this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants