Skip to content

fix: append file extension to WhatsApp document filename fallback#48745

Closed
Nic-vdwalt wants to merge 1 commit into
openclaw:mainfrom
Nic-vdwalt:fix/whatsapp-document-filename-extension
Closed

fix: append file extension to WhatsApp document filename fallback#48745
Nic-vdwalt wants to merge 1 commit into
openclaw:mainfrom
Nic-vdwalt:fix/whatsapp-document-filename-extension

Conversation

@Nic-vdwalt

Copy link
Copy Markdown

Summary

  • When sending documents without an explicit filename, recipients received files named "file" with no extension
  • Use extensionForMime() to derive the correct extension from the MIME type in both the auto-reply (deliver-reply.ts) and outbound send (send-api.ts) paths
  • In deliver-reply.ts, also check if the raw filename already has an extension before appending one

Test plan

  • Existing PDF fallback test updated to expect "file.pdf"
  • New text/csv test added expecting "file.csv" to verify dynamic extension derivation
  • All 3 tests in send-api.test.ts pass (npx vitest run src/web/inbound/send-api.test.ts)
  • Grepped for remaining bare "file" fallbacks — none found

🤖 Generated with Claude Code

When sending documents without an explicit filename, the recipient
received files named "file" with no extension. Use extensionForMime()
to derive the correct extension from the MIME type in both the
auto-reply and outbound send paths.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Mar 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a UX bug where WhatsApp document messages sent without an explicit filename were received as "file" with no extension. The fix adds MIME-type–to–extension derivation via the existing extensionForMime() helper in both the auto-reply delivery path (deliver-reply.ts) and the outbound send-API path (send-api.ts), with tests updated and extended to cover the new behaviour.

Key changes:

  • deliver-reply.ts: Introduces a two-step filename resolution — use the raw name as-is if it already carries an extension, otherwise append the MIME-derived extension (e.g. filefile.pdf).
  • send-api.ts: The fallback filename "file" is replaced with `file${extensionForMime(mediaType) ?? ""}`, so unknowns gracefully degrade back to plain "file".
  • send-api.test.ts: Updates the PDF assertion from "file" to "file.pdf" and adds a text/csv test to verify dynamic extension lookup.

Minor concern: The two code paths now diverge slightly — deliver-reply.ts also normalises an explicitly-provided filename that lacks an extension (e.g. "invoice""invoice.pdf"), while send-api.ts only applies the fix to the "file" fallback. If a caller passes a filename without an extension through the send-API, it will not be corrected. This is a low-priority inconsistency but worth aligning if the same callers drive both paths.

Confidence Score: 4/5

  • Safe to merge — focused, well-tested bug fix with no risk of regressions beyond a minor behavioural inconsistency between the two send paths.
  • The fix is narrow, uses an existing and already-tested helper (extensionForMime), and the test suite is updated to verify both the corrected behaviour and dynamic extension derivation. The one point deducted is for the inconsistency in how explicitly-provided filenames without extensions are treated across deliver-reply.ts vs send-api.ts.
  • src/web/inbound/send-api.ts — consider whether explicitly-provided filenames without extensions should also receive an appended extension, for consistency with deliver-reply.ts.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/web/inbound/send-api.ts
Line: 42

Comment:
**Inconsistency: explicit filenames without extensions not handled**

`deliver-reply.ts` applies the extension-appending logic to all filenames — including explicitly provided ones that lack an extension — via its `path.extname(rawName)` check. But here, if a caller passes `sendOptions.fileName = "invoice"` (no extension), it will be used as-is without appending the MIME-derived extension.

Whether this is intentional depends on the contract: callers of this API are presumably in control of the filename, but the two codepaths now behave differently for the same scenario. Consider aligning them:

```suggestion
          const rawName = sendOptions?.fileName?.trim() || "file";
          const fileName = path.extname(rawName) ? rawName : `${rawName}${extensionForMime(mediaType) ?? ""}`;
```

(This would also require importing `path` from `"node:path"` in this file.)

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 591270b

};
} else {
const fileName = sendOptions?.fileName?.trim() || "file";
const fileName = sendOptions?.fileName?.trim() || `file${extensionForMime(mediaType) ?? ""}`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Inconsistency: explicit filenames without extensions not handled

deliver-reply.ts applies the extension-appending logic to all filenames — including explicitly provided ones that lack an extension — via its path.extname(rawName) check. But here, if a caller passes sendOptions.fileName = "invoice" (no extension), it will be used as-is without appending the MIME-derived extension.

Whether this is intentional depends on the contract: callers of this API are presumably in control of the filename, but the two codepaths now behave differently for the same scenario. Consider aligning them:

Suggested change
const fileName = sendOptions?.fileName?.trim() || `file${extensionForMime(mediaType) ?? ""}`;
const rawName = sendOptions?.fileName?.trim() || "file";
const fileName = path.extname(rawName) ? rawName : `${rawName}${extensionForMime(mediaType) ?? ""}`;

(This would also require importing path from "node:path" in this file.)

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/web/inbound/send-api.ts
Line: 42

Comment:
**Inconsistency: explicit filenames without extensions not handled**

`deliver-reply.ts` applies the extension-appending logic to all filenames — including explicitly provided ones that lack an extension — via its `path.extname(rawName)` check. But here, if a caller passes `sendOptions.fileName = "invoice"` (no extension), it will be used as-is without appending the MIME-derived extension.

Whether this is intentional depends on the contract: callers of this API are presumably in control of the filename, but the two codepaths now behave differently for the same scenario. Consider aligning them:

```suggestion
          const rawName = sendOptions?.fileName?.trim() || "file";
          const fileName = path.extname(rawName) ? rawName : `${rawName}${extensionForMime(mediaType) ?? ""}`;
```

(This would also require importing `path` from `"node:path"` in this file.)

How can I resolve this? If you propose a fix, please make it concise.

@clawsweeper

clawsweeper Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge.

Summary
This PR appends MIME-derived extensions to WhatsApp document fallback filenames in legacy src/web auto-reply and send API paths and updates legacy send API tests.

Reproducibility: yes. by source inspection: current main still emits fileName: "file" for active WhatsApp document sends without a filename, and the active plugin test asserts that PDF fallback. I did not run a live WhatsApp delivery reproduction in this read-only review.

Real behavior proof
Needs real behavior proof before merge: No redacted real WhatsApp delivery proof is attached; the PR body only lists unit-test and grep checks, which do not prove recipient-visible behavior. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, ask a maintainer to comment @clawsweeper re-review.

Next step before merge
The branch needs human/contributor retargeting plus real WhatsApp proof; with the external proof gate missing, it should not be sent to an automated repair lane.

Security
Cleared: The diff only changes filename construction and legacy unit tests; it does not touch dependencies, CI, secrets, install scripts, package resolution, or code-execution surfaces.

Review findings

  • [P2] Port the fallback fix to the active WhatsApp plugin — src/web/inbound/send-api.ts:39
Review details

Best possible solution:

Retarget the MIME-derived fallback to the active WhatsApp plugin send and outbound media normalization paths, update focused plugin tests, and collect redacted live WhatsApp delivery proof.

Do we have a high-confidence way to reproduce the issue?

Yes, by source inspection: current main still emits fileName: "file" for active WhatsApp document sends without a filename, and the active plugin test asserts that PDF fallback. I did not run a live WhatsApp delivery reproduction in this read-only review.

Is this the best way to solve the issue?

No. The intended fix is narrow and reasonable, but this branch changes removed legacy src/web paths; the maintainable fix belongs in the active WhatsApp plugin paths with focused tests and real delivery proof.

Full review comments:

  • [P2] Port the fallback fix to the active WhatsApp plugin — src/web/inbound/send-api.ts:39
    This branch updates legacy src/web WhatsApp files that current main no longer tracks. Active document sends still go through extensions/whatsapp/src/inbound/send-api.ts and extensions/whatsapp/src/outbound-media-contract.ts, where the fallback can still emit file, so move the resolver and tests there for runtime impact.
    Confidence: 0.97

Overall correctness: patch is incorrect
Overall confidence: 0.94

Acceptance criteria:

  • node scripts/run-vitest.mjs extensions/whatsapp/src/inbound/send-api.test.ts extensions/whatsapp/src/auto-reply/deliver-reply.test.ts extensions/whatsapp/src/auto-reply.web-auto-reply.compresses-common-formats-jpeg-cap.test.ts
  • Attach redacted real WhatsApp delivery proof showing the received document filename after the patch.

What I checked:

Likely related people:

  • TsekaLuk: Merged WhatsApp filename preservation work changed the send path to use sendOptions.fileName while retaining the absent-filename "file" fallback. (role: introduced fallback behavior; confidence: high; commits: c54481155938; files: src/web/inbound/send-api.ts, src/web/outbound.ts)
  • mcaxtr: Authored the merged WhatsApp outbound media canonicalization that introduced extensions/whatsapp/src/outbound-media-contract.ts and routed active outbound/auto-reply media behavior through it. (role: current plugin media-contract owner; confidence: high; commits: 18c98316f7ac, 257cce661feb, 431f397cf79a; files: extensions/whatsapp/src/outbound-media-contract.ts, extensions/whatsapp/src/auto-reply/deliver-reply.ts, extensions/whatsapp/src/send.ts)
  • bobbyt74: Recent merged work touched the active WhatsApp send API and MIME fallback mapping near the helper this fix should use. (role: recent adjacent contributor; confidence: medium; commits: cae1d9bc6d6c; files: extensions/whatsapp/src/inbound/send-api.ts, extensions/whatsapp/src/send.ts, src/media/mime.ts)

Remaining risk / open question:

  • Recipient-visible WhatsApp filename behavior is still unverified because no redacted live delivery proof is attached.
  • Retargeting should account for adjacent open filename-sanitization work so extension fallback and filename safety do not diverge.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 54c9820ed9c2.

@clawsweeper clawsweeper Bot added the P2 Normal backlog priority with limited blast radius. label May 17, 2026
@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 17, 2026
@mcaxtr

mcaxtr commented May 17, 2026

Copy link
Copy Markdown
Member

Thanks for the PR and for working on this. We checked the current main branch, and this fix is already in main via #82851, which landed in commit 5040eb5.

WhatsApp document sends without explicit filenames now use MIME-aware fallback names such as file.pdf, file.csv, and file.png instead of extensionless file is already covered on main, so the behavior this PR changes is already handled there.

Because that has landed, I’m closing this PR as superseded by #82851.

Thanks again for the work here. If you think this closure is mistaken and your PR still fixes something meaningfully different on current main, feel free to open a new PR with that explanation.

@mcaxtr mcaxtr closed this May 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal backlog priority with limited blast radius. size: XS triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants