How a Missing Loop Cost Slack Users Their Multi-Image Messages

When you’re working on a messaging platform like openclaw, you quickly learn that assumptions kill features. Today’s story is about one of those assumptions—and how it silently broke an entire category of user uploads.
The bug was elegantly simple: resolveSlackMedia() was returning after downloading the first file from a multi-image Slack message. One file downloaded. The rest? Gone. Users sending those beloved multi-image messages suddenly found themselves losing attachments without any warning. The platform would process the first image, then bail out, leaving the rest of the MediaPaths, MediaUrls, and MediaTypes arrays empty.
Here’s where it gets interesting. The Telegram, Line, Discord, and iMessage adapters had already solved this exact problem. They’d all implemented the correct pattern: accumulate files into arrays, then return them all at once. But Slack’s implementation had diverged, treating the first successful download as a finish line rather than a waypoint.
The fix required two surgical changes. First, we rewired resolveSlackMedia() to collect all successfully downloaded files into arrays instead of returning early. This meant the prepare handler could now properly populate those three critical arrays—MediaPaths, MediaUrls, and MediaTypes—ensuring downstream processors (vision systems, sandbox staging, media notes) received complete information about every attachment.
But here’s where many developers would’ve stopped, and here’s where the second problem emerged. The next commit revealed an index alignment issue that could have shipped silently into production. When filtering MediaTypes with filter(Boolean), we were removing entries with undefined contentType values. The problem? That shrunk the array, breaking the 1:1 index correlation with MediaPaths and MediaUrls. Code downstream in media-note.ts and attachments.ts depends on those arrays being equal length—otherwise, MIME type lookups fail spectacularly.
The solution was counterintuitive: replace the filter with a nullish coalescing fallback to “application/octet-stream”. Instead of removing entries, we’d preserve them with a sensible default. Three arrays, equal length, synchronized indices. Simple once you see it.
This fix resolved issues #11892 and #7536, affecting real users who’d been mysteriously losing attachments. It’s a reminder that symmetry matters in data structures—especially when multiple systems depend on that symmetry. And sometimes the best code is the one that matches the pattern already proven to work elsewhere in your codebase.
Speaking of patterns: .NET developers are picky when it comes to food. They only like chicken NuGet. 😄
Metadata
- Branch:
- main
- Dev Joke
- Почему Sentry не пришёл на вечеринку? Его заблокировал firewall