Error when uploading png file

I am attempting to upload a number of files using the v2 endpoint, but it seems to fail whenever the file type is a png. I get this response from the server:

{
  error_code: 'RecordInvalidException',
  status_code: 422,
  error_message: 'Validation failed: Resource Paperclip::Errors::NotIdentifiedByImageMagickError, Resource Paperclip::Errors::NotIdentifiedByImageMagickError, Resource Paperclip::Errors::NotIdentifiedByImageMagickError, Resource Paperclip::Errors::NotIdentifiedByImageMagickError, Resource Paperclip::Errors::NotIdentifiedByImageMagickError, Resource Paperclip::Errors::NotIdentifiedByImageMagickError',
  error_data: {
    error_data: 'Validation failed: Resource Paperclip::Errors::NotIdentifiedByImageMagickError, Resource Paperclip::Errors::NotIdentifiedByImageMagickError, Resource Paperclip::Errors::NotIdentifiedByImageMagickError, Resource Paperclip::Errors::NotIdentifiedByImageMagickError, Resource Paperclip::Errors::NotIdentifiedByImageMagickError, Resource Paperclip::Errors::NotIdentifiedByImageMagickError'
  }
}

Has anyone seen this before? I’m not really sure what is causing this issue and google hasn’t been much help.

Thanks,
Jon

Looks like monday is using imageMagick (see ImageMagick – Convert, Edit, or Compose Digital Images) and that service returns the error to monday. See also ruby on rails - Paperclip error - NotIdentifiedByImageMagickError - Stack Overflow for a discussion on that error.

Hope that points you in the right direction.

1 Like

Hello @jmsmit38 !

Are you uploading the images to an update or to a file column?

Do you have one of these pngs that can be shared with me so I can try to do it on my end?

Cheers,
Matias

1 Like

Hi @basdebruin and @Matias.Monday,

Thanks for the replies. I think @basdebruin is correct. It seems that the Monday server is returning a error from the ImageMagick library whenever I attempt to upload a PNG file.

To expand a little bit, I am running a script that is transferring PNG files from one board to another. The workflow is:

  1. Obtain the URL from the original board.
  2. Download file
  3. Upload file to new board
    The file is being uploaded to a column. I don’t think it is a problem with my code, as pdf files work without issue. Unless there is something special we need to do with PNGs that I’m not aware of, I believe that the code should be the same regardless of file type. I also don’t think the PNG is malformed, as it was uploaded to the original board through the browser without issue. There seems to be a difference between the v2 file endpoint in the API, and the way the browser handles the file upload.

I have attached a sample PNG if it helps.

Thanks,
Jon

It looks like ImageMagick will throw this error for a variety of reasons, from malformed files to dependency issues.

Hello again @jmsmit38!

This is very strange. I tested this with the file you sent and it worked for me:

What happens if you try to do this via Postman?

Hey Matias, It does work with Postman… Perhaps it’s the nodejs libraries I am using that cause a malformation of the png file?

Here are the helper functions and libraries I use:

const fetch = require('fetch-timeout');
const https = require('https'); // or 'https' for https:// URLs
const fs = require('fs').promises;
const fss = require('fs');

const mondayFile = async (name, contractURL, itemID, columnID) => {
  return new Promise(function(resolve) {
    try {
      https.get(contractURL, async function(response) {
        const file = fss.createWriteStream("/tmp/" + name);
        response.pipe(file);
        file.on('finish', async () => {
          console.log('Download Completed');
          let resp = await uploadFile(name, itemID, columnID);
          let prnt = await resp.json();
          console.log(prnt);
          if (typeof prnt.data === 'undefined') {console.log("no data...");throw new Error("no data...");}
          resolve({'status' : 'Success'});
        })
      });
      
    }
    catch (e) {
      console.warn(e);
      throw new Error(e);
    }
  });
};

const uploadFile = async (name, itemID, columnID) => {
  const url = 'https://api.monday.com/v2/file';
  const query = `mutation ($file: File!) { add_file_to_column(item_id: ${itemID}, column_id: ${columnID}, file: $file) {id} }`;
  var data = "";
  var upfile = "/tmp/" + name;
  const boundary = "xxxxxxxxxxxxxxx";

  console.log("sending file");
  const content = await fs.readFile(upfile)
  //below, we will construct a multipart request. Take a look at the "name" within each part, as those will refer to different parts of the API call.
  // construct query part
  data += "--" + boundary + "\r\n";
  data += "Content-Disposition: form-data; name=\"query\"; \r\n";
  data += "Content-Type:application/json\r\n\r\n";
  data += "\r\n" + query + "\r\n";
  
  // construct variables part
  data += "--" + boundary + "\r\n";
  data += "Content-Disposition: form-data; name=\"variables[file]\"; filename=\"" + upfile + "\"\r\n";
  data += "Content-Type:application/octet-stream\r\n\r\n";
  var payload = Buffer.concat([
          Buffer.from(data, "utf8"),
          new Buffer.from(content, 'binary'),
          Buffer.from("\r\n--" + boundary + "--\r\n", "utf8"),
  ]);

  // construct request options
  var options = {
      method: 'post',
      headers: {
        "Content-Type": "multipart/form-data; boundary=" + boundary,
        "Authorization" : process.env.MONDAY_TOKEN
      },
      body: payload,

    // make request
  }
  return await fetch(url, options, 40000, 'file upload timeout exceeded on item: ' + itemID)
  .catch(function(err) { console.warn(err); throw new Error(err);});
};

Have you tried changing the MIME type by replacing this:

with:

That doesn’t seem to help unfortunately… I think I’m going to need to see exactly what postman is doing differently when it constructs the multi-part request vs what my code is doing.

@Matias.Monday @basdebruin I just replicated the exact form data that is being output by postman in the request that succeeds, and my code is still failing. The only thing I can think of is that fss.createWriteStream and fs.readFile need a specific encoding other than the default. I tried a few, from base64 to binary, and none of them seem to work. Whatever it is, I’m fairly certain the problem has been narrowed down to how the file is being encoded.

@Matias.Monday @basdebruin

It didn’t even occur to me to check the file size. The file size is 0. Which is confirmed by the fact that the pdf that was ‘successfully’ uploading, is in fact empty.
It also didn’t occur to me that the file endpoint that I am pulling from in the column, is only accessible if you are authenticated.
Now my question becomes, how do we provide authentication credentials for the download? I am running this script in a lambda function on AWS, so I was hoping to just pass an auth token in the header, but that doesn’t seem to work.
At least I’m heading in the right direction now I think. I spent way too much time tearing about my multipart request and rebuilding it different ways :upside_down_face:

Thanks,
Jon

@jmsmit38
Yeah, it is easy to overlook small thing. Authentication is dependent on what type of an app you are building. In don’t have Lamda experience as I run everything on NodeJS on my hosted VPS (under Plesk). If you are building an integration app you could use the shortLivedToken provided in every post monday makes to your endpoint. You need to decode that with your client secret and pass the token to the monday API calls. If you are building views I don’t think there is a need to do authentication as your app runs within the monday board environment. Hope this make sense.

1 Like

@basdebruin
It’s actually just a script for data migration, thus there wouldn’t be a shortLivedToken since its not an integration app. I’m not sure either of your scenarios really apply to this. It’s really just a one off script that needs to run because we re-designed the architecture of our workspace and had to build new boards in a way where we couldn’t just send the items over to the new workspace with all of the data in place (I hope I’m making sense here… there were a lot of connected boards with data scattered across numerous items for every project). Is there an easier way to transfer the files from one board to another? It would be cool if since the file is already uploaded to Monday servers, you could just assign the URL to the cell, but their public API doesn’t have the ability to do that from what I can tell.

Thanks,
Jon

hi @jmsmit38
It would be very cool if you could just copy the assetID to another board / column but unfortunately that is not possible (yet?). I did try that and it looks like it is working (file is visible) but the file in the target location will not open.
If this is just a one time I would send a user token (admin perhaps) in the request. You can copy that token if you go to developers section in monday.

1 Like

@basdebruin I got an access denied error with the user token, but I was able to find a workaround. I downloaded the file through the browser, copied the cookie string from the request, and pasted it into the request in my script. I also had to add a library called follow-redirects, because the endpoint that is returned from the file column is not the actual location of the file.
Do you know how long the session token cookie is valid for? And is there any way to obtain one from the user token?

Thanks for the support @Matias.Monday @basdebruin

hi @jmsmit38, I am wondering if you need to use a session token here. The user token should do it and does not expire. You have to set the user token in the Authorization header and I see you are setting it to:

"Authorization" : process.env.MONDAY_TOKEN

Does process.env.MONDAY_TOKEN contains the token found in My Tokens in the Developer environment?

That is correct. I tried both the API token and the User token and neither work here. The only way I found to do it was by setting the cookie string with the session token.