title: JavaScript fetch() Discord file upload results in a HTTP 400 "cannot send empty message, 50006" error
body: <p>I am trying to upload a single PNG file to a Discord channel using a webhook using vanilla javascript because I cannot use Discord.js.</p>
  <p>In Chrome's JavaScript console, I get the following error message:</p>
  <blockquote>
  <p>POST <a href="https://discordapp.com/api/webhooks/" rel="nofollow noreferrer">https://discordapp.com/api/webhooks/</a>&lt;my discord webhook&gt; 400</p>
  </blockquote>
  <p>Followed by this <code>console.log()</code> output:</p>
  <blockquote>
  <p>Success: {message: &quot;Cannot send an empty message&quot;, code: 50006}</p>
  </blockquote>
  <p>I include my code and the Network console transcript at the end, but I have 1 question:</p>
  <ul>
  <li>what am I doing wrong? I see that <code>Content-Disposition</code> for the file is of type <code>form-data</code>, any connection to my problem?</li>
  </ul>
  <p>Before that, I managed to post messages (json only, no files) using both XMLHttpRequest and fetch.</p>
  <p>I did not try using JQuery although I could and would prefer a pure Javascript solution.</p>
  <p>Below is the code I wrote, to send a single PNG image as well as a payload_json JSON data payload (same error without the payload):</p>
  <pre class="lang-js prettyprint-override"><code>    const filename = 'image.png';
      domtoimage.toBlob(document.querySelector('&lt;element to snapshot selector&gt;'))
        .then(function (blob) {
          const formData = new FormData();
          var params = {
            username: &quot;My bot name&quot;,
            avatar_url: &quot;&quot;,
            content: &quot;another test 2&quot;,
            embeds: [{
              &quot;image&quot;: {
                &quot;url&quot;: &quot;https://i.imgur.com/ZGPxFN2.jpg&quot;
              }
            }]
          };
          formData.append('payload_json',JSON.stringify(params))
          formData.append('file',blob);
          fetch('https://discordapp.com/api/webhooks/&lt;my discord webhook&gt;', {
            method: 'POST',
            headers: {
              'Content-Type': 'multipart/form-data',
            },
            body: formData,
          })
          .then(response =&gt; response.json())
          .then(result =&gt; {
            console.log('Success:', result);
          })
          .catch(error =&gt; {
            console.error('Error:', error);
          });
        });
  </code></pre>
  <p>In Chrome's Network console, I get the following information:</p>
  <pre><code>General
      Request URL: https://discordapp.com/api/webhooks/&lt;my discord webhook&gt;
      Request Method: POST
      Status Code: 400 
      Remote Address: 162.159.134.233:443
      Referrer Policy: no-referrer-when-downgrade
  Response Headers
      access-control-allow-credentials: true
      access-control-allow-headers: Content-Type, Authorization, X-Track, X-Super-Properties, X-Context-Properties, X-Failed-Requests, X-Fingerprint, X-RPC-Proxy, X-Debug-Options, x-client-trace-id, If-None-Match, X-RateLimit-Precision
      access-control-allow-methods: POST, GET, PUT, PATCH, DELETE
      access-control-allow-origin: &lt;access-control-allow-origin&gt;
      access-control-expose-headers: Retry-After, X-RateLimit-Global, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-RateLimit-Bucket, Date
      cf-cache-status: DYNAMIC
      cf-ray: &lt;cf-ray&gt;
      cf-request-id: &lt;cf-request-id&gt;
      content-length: 58
      content-type: application/json
      date: Thu, 16 Jul 2020 04:23:41 GMT
      expect-ct: max-age=604800, report-uri=&quot;https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct&quot;
      server: cloudflare
      set-cookie: __cfduid=&lt;__cfduid&gt;; expires=Sat, 15-Aug-20 04:23:40 GMT; path=/; domain=.discordapp.com; HttpOnly; SameSite=Lax
      set-cookie: __cfruid=&lt;__cfduid&gt;; path=/; domain=.discordapp.com; HttpOnly; Secure; SameSite=None
      status: 400
      strict-transport-security: max-age=31536000; includeSubDomains
      via: 1.1 google
      x-envoy-upstream-service-time: 27
      x-ratelimit-bucket: &lt;x-ratelimit-bucket&gt;
      x-ratelimit-limit: 5
      x-ratelimit-remaining: 4
      x-ratelimit-reset: &lt;x-ratelimit-reset&gt;
      x-ratelimit-reset-after: 2
  Request Headers
      :authority: discordapp.com
      :method: POST
      :path: /api/webhooks/&lt;my discord webhook&gt;
      :scheme: https
      accept: */*
      accept-encoding: gzip, deflate, br
      accept-language: en,en-US;q=0.9,ja;q=0.8
      content-length: 50696
      content-type: multipart/form-data
      origin: https://&lt;origin&gt;
      referer: https://&lt;referer&gt;
      sec-fetch-dest: empty
      sec-fetch-mode: cors
      sec-fetch-site: cross-site
      user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
  Request Payload
      ------WebKitFormBoundarypu7GvABzASKKKTQF
      Content-Disposition: form-data; name=&quot;payload_json&quot;
  
      {&quot;username&quot;:&quot;WhiteBoard&quot;,&quot;avatar_url&quot;:&quot;https://i.imgur.com/ZGPxFN2.jpg&quot;,&quot;content&quot;:&quot;another test 2&quot;,&quot;embeds&quot;:[{&quot;image&quot;:{&quot;url&quot;:&quot;https://i.imgur.com/ZGPxFN2.jpg&quot;}}]}
      ------WebKitFormBoundarypu7GvABzASKKKTQF
      Content-Disposition: form-data; name=&quot;file&quot;; filename=&quot;blob&quot;
      Content-Type: image/png
  
      ‰PNG
      
  
      IHDRpíã IDAT[...]
      [...]:cjIEND®B`‚
      ------WebKitFormBoundarypu7GvABzASKKKTQF--
  </code></pre>
  <p><strong>-------- Edit (2020/07/16 14:30): Answering my secondary question myself --------</strong></p>
  <ul>
  <li>why is the filename part in the request payload &quot;blob&quot;?
  <code>FormData.append()</code> accepts a third parameter with the filename, my code should look like below:</li>
  </ul>
  <pre><code>    formData.append('file',blob,filename);
  </code></pre>