Discord bot not displaying animated GIFs from Giphy API

I’m working on a Discord bot using JavaScript that fetches random GIFs through the Giphy API. The issue I’m facing is that when my bot tries to display the GIF in the chat, nothing appears. The message shows up completely empty, not even displaying the GIF link.

I can confirm the API call is working because the console logs show the correct URL:

Fetched GIF URL: https://giphy.com/gifs/funny-cat-memes-xyz123abc

But in Discord chat, it’s just blank space. What’s weird is that Discord’s built-in /giphy command works perfectly and uses similar URLs.

Here’s my function to get GIFs from Giphy:

// Fetches random GIF from Giphy API
const fetchRandomGif = async () => {
    try {
        const apiCall = await fetch(`https://api.giphy.com/v1/gifs/random?api_key=${process.env.GIPHY_KEY}&tag=&rating=PG`,
        {
            method: 'GET',
            headers: {
              'Content-Type': 'application/json'
            },
        });
        const jsonData = await apiCall.json();
        console.log('response data:');
        console.log(jsonData);
        console.log('gif info:');
        console.log(jsonData['data']);
        console.log('gif link:');
        console.log(jsonData['data']['url']);
        console.log(`Fetched GIF URL: ${jsonData['data']['url']}`);
        return jsonData['data']['url'];
    }
    catch {
        console.log('Failed to retrieve GIF');
    }
}

And here’s where I handle the command:

// Bot message handler
client.on('message', async msg => {
    if (msg.content === 'hello') {
        msg.reply('hi there!');
    }
    else if (msg.content === '!joke') {
        var joke = await getRandomJoke();
        msg.reply(joke);
    }
    else if (msg.content === '!motivate') {
        msg.reply(getMotivationalQuote());
    }
    else if (msg.content === '!meme') {
        msg.reply('fetching meme...');
        var meme_url = fetchRandomGif();
        msg.reply(fetchRandomGif());
    }
});

Any idea what I’m missing here?

Had this same problem before. Your command handler is the issue - you’re calling fetchRandomGif() twice without awaiting either one. When you don’t await an async function, it just returns a Promise object, so Discord sees empty content. Your fetchRandomGif function structure is fine, but like others said, you need the direct image URL from jsonData.data.images.original.url instead of the webpage URL. Also throw in some error handling since network calls can fail. One more thing - sending two replies back-to-back looks spammy. Either edit the first message instead of sending a second one, or skip the “fetching” message and just send the GIF directly.

Two things broke your bot. You’re calling an async function without await, and you’re grabbing the wrong URL from the response.

Look at your message handler:

var meme_url = fetchRandomGif(); // Returns Promise, not URL
msg.reply(fetchRandomGif()); // Another Promise

Both calls return Promise objects instead of actual URLs. Discord gets empty content.

Fix the async issue:

else if (msg.content === '!meme') {
    const gifUrl = await fetchRandomGif();
    msg.reply(gifUrl);
}

Then update your function to return the direct image URL:

return jsonData['data']['images']['original']['url'];

The URL you’re using (jsonData['data']['url']) points to the Giphy webpage. Discord needs the actual GIF file to embed it.

I’ve hit this exact issue tons of times. The Promise thing catches everyone when they’re starting with Discord bots. Console logging helps, but async functions are tricky - they’ll always return something even when they break.

you’re grabbin jsonData['data']['url'] which just gives you the giphy webpage link, not the actual gif. discord needs the direct image url to show it properly. use jsonData.data.images.original.url instead - that’s the actual gif file discord can display.

Two problems here. First, you’re not awaiting your async function. Second, you’re using the wrong URL property.

Your code calls fetchRandomGif() without await, so it returns a Promise instead of the actual URL. Discord can’t display a Promise.

Also, jsonData['data']['url'] gives you the Giphy webpage link. You want jsonData['data']['images']['original']['url'] for the direct GIF file.

Honestly though, building Discord bots this way gets messy fast. I used to write similar bots and wasted tons of time debugging API issues and handling errors.

What changed everything was switching to automation. Now I use Latenode to build Discord bots without dealing with async problems or API parsing headaches.

You create a visual workflow that handles the Giphy API call, extracts the right URL automatically, and sends it to Discord. If the API fails, you can add fallback logic or retry attempts without writing exception handling code.

Best part is you can add features like rate limiting or content filtering just by dragging components. No more digging through logs wondering why your bot broke.

You’re not awaiting the async function. Look at this:

var meme_url = fetchRandomGif();
msg.reply(fetchRandomGif());

You’re calling fetchRandomGif() without await, so it returns a Promise instead of the actual URL. That’s why Discord shows nothing.

Fix it:

else if (msg.content === '!meme') {
    msg.reply('fetching meme...');
    var meme_url = await fetchRandomGif();
    msg.reply(meme_url);
}

Also, wrong URL format. Use jsonData['data']['images']['original']['url'] instead of jsonData['data']['url'] to get the direct GIF file.

Honestly though, managing Discord bots manually gets messy fast. I’ve been there. What actually works is automating the whole thing.

I use Latenode for Discord bot operations. It handles the API calls, processes responses, and manages errors automatically. Set up the flow once and it just works. No more debugging async issues or URL parsing problems.

The visual workflow makes it clear what’s happening at each step. You can add features like GIF caching or fallback responses without writing more code.

Classic mistake - you’re calling fetchRandomGif() twice and not awaiting either one. That second call isn’t even needed. Just do await fetchRandomGif() once, store it in a variable, then use that for your reply.