Skip to content

fix: decode URL-encoded Slack audio filenames#118

Open
sweetmantech wants to merge 1 commit intomainfrom
fix/decode-audio-filename
Open

fix: decode URL-encoded Slack audio filenames#118
sweetmantech wants to merge 1 commit intomainfrom
fix/decode-audio-filename

Conversation

@sweetmantech
Copy link
Copy Markdown
Contributor

@sweetmantech sweetmantech commented Mar 31, 2026

Summary

  • Slack generates URLs with %20-encoded spaces in filenames
  • When passed to fal.ai Whisper, the encoded filename causes ApiError: Bad Request
  • Add decodeURIComponent() to decode the filename before passing to transcription

Context

Triggered by prod error: run_cmnf0e48g9zqw0on6kv3z4iw4 — file Singin%20in%20the%20Rain%202.mp3

Test plan

  • New test: "decodes URL-encoded filenames from Slack URLs"
  • 210/210 tests pass

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of special characters in audio file names imported from URLs. Audio files previously displayed with URL-encoded characters now show correctly decoded filenames and titles. For example, spaces that were displayed as "%20" now appear as actual spaces, ensuring better readability and consistency throughout the application.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 31, 2026

Warning

Rate limit exceeded

@sweetmantech has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 56 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 9 minutes and 56 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 84113298-579d-47f1-8fe3-e41ef24e102e

📥 Commits

Reviewing files that changed from the base of the PR and between 542b100 and 43a13d5.

📒 Files selected for processing (3)
  • src/content/__tests__/selectAttachedAudioClip.test.ts
  • src/content/selectAttachedAudioClip.ts
  • src/content/transcribeSong.ts
📝 Walkthrough

Walkthrough

The changes add URL decoding functionality to the selectAttachedAudioClip function to properly handle percent-encoded characters in audio filenames. A new test case verifies that URL-encoded filenames (e.g., containing %20 for spaces) are correctly decoded to human-readable format before being used for title and filename extraction.

Changes

Cohort / File(s) Summary
URL Decoding in Audio Clip Selection
src/content/selectAttachedAudioClip.ts, src/content/__tests__/selectAttachedAudioClip.test.ts
Added decodeURIComponent call to decode URL-encoded filenames before extracting song title. New test case verifies that percent-encoded characters like %20 are properly converted to spaces in both songFilename and songTitle.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~5 minutes

Poem

🐰 A whisker-twitch of joy, I say,
When %20 becomes a space today,
No more encoded woes in sight,
Our filenames now decoded right!
Clear as clover, plain and true, 🌱

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: decoding URL-encoded filenames from Slack audio URLs, which directly addresses the production issue and aligns with both code modifications.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/decode-audio-filename

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/content/selectAttachedAudioClip.ts`:
- Line 35: The line computing songFilename in selectAttachedAudioClip uses
decodeURIComponent on a user-controlled urlPath which can throw on malformed
percent-encoding; wrap the decodeURIComponent call in a try/catch and on
URIErrors (or any exceptions) fall back to the raw filename (the result of
urlPath.split("/").pop() or "attached-audio.mp3") so the function doesn't throw
for invalid percent-encoding in audioUrl.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cb768f30-caba-4966-bf96-28c0cd316ba0

📥 Commits

Reviewing files that changed from the base of the PR and between d974928 and 542b100.

📒 Files selected for processing (2)
  • src/content/__tests__/selectAttachedAudioClip.test.ts
  • src/content/selectAttachedAudioClip.ts

// Derive filename from URL
const urlPath = new URL(audioUrl).pathname;
const songFilename = urlPath.split("/").pop() ?? "attached-audio.mp3";
const songFilename = decodeURIComponent(urlPath.split("/").pop() ?? "attached-audio.mp3");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Does JavaScript decodeURIComponentthrow aURIErroron malformed percent-encoded input (for example%ZZ), and what is the recommended defensive handling pattern?

💡 Result:

Yes. decodeURIComponent must throw a URIError when it encounters malformed percent-encoding—e.g. a % not followed by two hex digits (so %ZZ, %, %A, etc.), or when the decoded bytes are not valid UTF‑8. [1], [2]

Defensive handling pattern (recommended): wrap decoding in try/catch, and treat URIError as “bad input” (return a fallback, or surface a validation error), while rethrowing other exceptions.

export function safeDecodeURIComponent(s, fallback = s) {
  try {
    return decodeURIComponent(s);
  } catch (e) {
    if (e instanceof URIError) return fallback; // or: return null / throw new Error("Bad encoding")
    throw e;
  }
}

Sources: MDN decodeURIComponent() (exceptions) [1]; ECMAScript spec “Decode” operation (throws URIError on non-hex escapes / invalid UTF‑8) [2].

[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent
[2] https://www.ecma-international.org/wp-content/uploads/ECMA-262_5th_edition_december_2009.pdf


🏁 Script executed:

cat -n src/content/selectAttachedAudioClip.ts

Repository: recoupable/tasks

Length of output: 3500


Guard decodeURIComponent against malformed filenames.

The audioUrl parameter accepts user-provided URLs. If a URL's pathname contains invalid percent-encoding (e.g., %ZZ or incomplete escapes), decodeURIComponent throws a URIError and halts execution. Wrap the call in a try/catch block and fall back to the raw filename on encoding errors.

Suggested patch
-  const songFilename = decodeURIComponent(urlPath.split("/").pop() ?? "attached-audio.mp3");
+  const rawFilename = urlPath.split("/").pop() ?? "attached-audio.mp3";
+  let songFilename = rawFilename;
+  try {
+    songFilename = decodeURIComponent(rawFilename);
+  } catch {
+    // Keep raw filename if encoding is malformed
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const songFilename = decodeURIComponent(urlPath.split("/").pop() ?? "attached-audio.mp3");
const rawFilename = urlPath.split("/").pop() ?? "attached-audio.mp3";
let songFilename = rawFilename;
try {
songFilename = decodeURIComponent(rawFilename);
} catch {
// Keep raw filename if encoding is malformed
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/content/selectAttachedAudioClip.ts` at line 35, The line computing
songFilename in selectAttachedAudioClip uses decodeURIComponent on a
user-controlled urlPath which can throw on malformed percent-encoding; wrap the
decodeURIComponent call in a try/catch and on URIErrors (or any exceptions) fall
back to the raw filename (the result of urlPath.split("/").pop() or
"attached-audio.mp3") so the function doesn't throw for invalid percent-encoding
in audioUrl.

@sweetmantech sweetmantech force-pushed the fix/decode-audio-filename branch from 542b100 to 672efc7 Compare March 31, 2026 19:39
…Song

- Decode URI components and replace spaces with hyphens in audio filenames
- Add detailed logging around fal.storage.upload and fal-ai/whisper calls
  to diagnose Bad Request errors (logs filename, size, audioUrl, error)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sweetmantech sweetmantech force-pushed the fix/decode-audio-filename branch from 672efc7 to 43a13d5 Compare March 31, 2026 19:43
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

Successfully merging this pull request may close these issues.

1 participant