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

[500 Error] Unable to upload any Media Attachments #1256

Open
ZachHandley opened this issue Jan 2, 2025 · 3 comments
Open

[500 Error] Unable to upload any Media Attachments #1256

ZachHandley opened this issue Jan 2, 2025 · 3 comments

Comments

@ZachHandley
Copy link

I can't upload any images whatsoever to Mastodon

I've tried so many different methods, this stupid thing just keeps returning

MastoHttpError: Error processing thumbnail for uploaded media
    at HttpNativeImpl.<anonymous> (file:///usr/local/server/src/function/node_modules/masto/dist/index.js:1000:24)
    at Generator.next (<anonymous>)
    at fulfilled (file:///usr/local/server/src/function/node_modules/masto/dist/index.js:73:58)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

so idk. I'm on ^6.10.1, posting statuses is fine, and I've tried giving it a thumbnail (which says there can't be one for images), I've tried giving it a local file, a blob, everything, and it still fails over and over.

async uploadMediaAttachmentCustom(
    mediaAttachment: MastodonMediaAttachment
  ): Promise<mastodon.v1.MediaAttachment> {
    const mastodonClient = this.getMastodonApiClient();
    let file: Buffer | Blob;
    let fileMimeType: string;
    let thumbnail: Buffer | Blob | undefined;
    let thumbnailMimeType: string | undefined;

    // Handle main file
    this.log(`Handling main file`);
    if (typeof mediaAttachment.file === 'string') {
      this.log(
        `Downloading image from URL, ${mediaAttachment.file.slice(0, 100)}`
      );
      const result = await this.appwriteManager.imageUtils.downloadBlobFromUrl(
        mediaAttachment.file
      );
      file = result.blob;
      fileMimeType = result.mimeType;
    } else if (mediaAttachment.file instanceof Blob) {
      this.log(`Handling Blob file`);
      file = mediaAttachment.file; // Use the Blob directly instead of converting
      fileMimeType = mediaAttachment.file.type;
    } else {
      throw new Error('Invalid file type for media attachment');
    }

    // Handle thumbnail
    if (mediaAttachment.thumbnail) {
      this.log(`Handling thumbnail`);
      try {
        if (typeof mediaAttachment.thumbnail === 'string') {
          this.log(
            `Downloading thumbnail from URL, ${mediaAttachment.thumbnail.slice(0, 100)}`
          );
          const result =
            await this.appwriteManager.imageUtils.downloadBlobFromUrl(
              mediaAttachment.thumbnail
            );
          thumbnail = result.blob;
          thumbnailMimeType = result.mimeType;
        } else if (mediaAttachment.thumbnail instanceof Blob) {
          this.log(`Handling Blob thumbnail`);
          thumbnail = mediaAttachment.thumbnail; // Use the Blob directly
          thumbnailMimeType = mediaAttachment.thumbnail.type;
        }
      } catch (error) {
        this.log(`Error downloading thumbnail: ${error}`);
        thumbnail = undefined;
        thumbnailMimeType = undefined;
      }
    }

    const mediaParams: mastodon.rest.v2.CreateMediaAttachmentParams = {
      file:
        file instanceof Buffer
          ? new Blob([file], { type: fileMimeType })
          : file,
      thumbnail:
        thumbnail instanceof Buffer
          ? new Blob([thumbnail], { type: thumbnailMimeType })
          : thumbnail,
      description: mediaAttachment.description,
      focus: mediaAttachment.focus,
    };

    this.log(
      `Uploading media with params: ${JSON.stringify(
        {
          fileType: fileMimeType,
          thumbnailType: thumbnailMimeType,
          hasFile: !!file,
          hasThumbnail: !!thumbnail,
          description: mediaAttachment.description,
          focus: mediaAttachment.focus,
        },
        null,
        2
      )}`
    );

    const response = await mastodonClient.v2.media.create(mediaParams);
    return response;
  }
@ZachHandley ZachHandley changed the title Unable to upload any Media Attachments [500 Error] Unable to upload any Media Attachments Jan 2, 2025
@neet
Copy link
Owner

neet commented Jan 3, 2025

I would like to ensure the following points to breakdown the issue:

  • What happens if you omit the type property from your Blob constructor?
  • What happens if you omit a thumbnail, description, and focus?
    • If you managed to upload the image with this, it is highly likely that something is wrong with these properties. Try double-checking the values you are passing.
  • Is your downloaded image via Appwrite valid? Could you try saving it to a local disk and opening it?

If you tried all of the above and the problem is still present, try calling Mastodon API directly by the following code to check if it can be solved if you don't use Masto.js:

// test.js
async function main() {
  const file = await loadYourFileHere()

  const fd = new FormData();
  fd.append('file', new Blob([file]));
  
  const response = await fetch(`${process.env.MASTODON_URL}/api/v2/media`, {
    method: "POST",
    headers: {
      "Authorization": "Bearer " + process.env.MASTODON_TOKEN,
    },
    body: fd,
  });
  
  console.log(await response.json());
}  

main();
MASTODON_URL=https://mastodon.social MASTODON_TOKEN=foo node ./test.js

If your request succeeded with the code above but not with Masto.js, I will inspect for the details of this issue. If error still present, it is a Mastodon issue so go to their GitHub repository and open an issue there.

@ZachHandley
Copy link
Author

  async uploadMediaAttachmentCustom(
    mediaAttachment: MastodonMediaAttachment
  ): Promise<mastodon.v1.MediaAttachment> {
    const mastodonClient = this.getMastodonApiClient();
    let file: Buffer | Blob;
    let fileMimeType: string;

    // Handle main file
    this.log(`Handling main file`);
    try {
      if (typeof mediaAttachment.file === 'string') {
        this.log(
          `Downloading image from URL: ${mediaAttachment.file.slice(0, 100)}`
        );
        const result =
          await this.appwriteManager.imageUtils.downloadBlobFromUrl(
            mediaAttachment.file
          );

        // Test the downloaded blob
        this.log(`Downloaded blob size: ${result.blob.size}`);
        this.log(`Downloaded blob type: ${result.mimeType}`);

        // Create a new blob without specifying type
        file = new Blob([result.blob]);
        fileMimeType = result.mimeType;

        this.log(
          `Created new blob - size: ${file.size}, type: ${file.type || 'not specified'}`
        );
      } else if (mediaAttachment.file instanceof Blob) {
        this.log(
          `Handling Blob file - size: ${mediaAttachment.file.size}, type: ${mediaAttachment.file.type}`
        );
        file = mediaAttachment.file;
        fileMimeType = mediaAttachment.file.type;
      } else {
        throw new Error('Invalid file type for media attachment');
      }

      // Try direct API call first
      try {
        this.log('Attempting direct API upload...');
        const formData = new FormData();
        formData.append('file', file);

        const response = await fetch(`https://mastodon.social/api/v2/media`, {
          method: 'POST',
          headers: {
            Authorization: `Bearer ${this.oAuthAccount.accessToken}`,
          },
          body: formData,
        });

        if (!response.ok) {
          const errorText = await response.text();
          throw new Error(
            `Direct API upload failed: ${response.status} - ${errorText}`
          );
        }

        const result = await response.json();
        this.log(`Direct API upload successful: ${JSON.stringify(result)}`);
        return result;
      } catch (directApiError) {
        this.log(
          `Direct API upload failed, falling back to masto.js: ${directApiError}`
        );

        // Fall back to masto.js
        const mediaParams: mastodon.rest.v2.CreateMediaAttachmentParams = {
          file: file instanceof Buffer ? new Blob([file]) : file,
          // Only include optional parameters if they exist
          ...(mediaAttachment.description && {
            description: mediaAttachment.description,
          }),
          ...(mediaAttachment.focus && { focus: mediaAttachment.focus }),
        };

        this.log(
          `Uploading media with params: ${JSON.stringify(
            {
              fileType: fileMimeType,
              hasFile: !!file,
              fileSize: file.size,
              description: mediaAttachment.description,
              focus: mediaAttachment.focus,
            },
            null,
            2
          )}`
        );

        const response = await mastodonClient.v2.media.create(mediaParams);
        this.log(`Masto.js upload successful: ${JSON.stringify(response)}`);
        return response;
      }
    } catch (error) {
      this.log(`Error in uploadMediaAttachmentCustom: ${error}`);
      throw error;
    }
  }

I updated my code to use the direct post by sending the request, and it worked, so it's on masto.js I think

@neet
Copy link
Owner

neet commented Jan 4, 2025

@ZachHandley Your reproduction code does not conclusively prove that Masto.js caused the error, as description and focus are included only in the Masto.js request. To ensure both Masto.js and direct API calls are tested under the same conditions, try either removing description and focus from both, or adding them to both.

It would also be helpful if you could provide the actual URL of the instance, the image you used to reproduce the error, and the values for description and focus.

Additionally, your reproduction code appears to be part of a class, but other methods (getMastodonApiClient, appwriteManager) are not defined in the snippet you provided. Please share a minimal, reproducible example so I can test it under the same conditions.

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