#21881: fix(whatsapp): pass user-provided fileName through document send pipe…
channel: whatsapp-web
app: web-ui
gateway
size: XS
Cluster:
WhatsApp Document Handling Fixes
## Summary
- Threads the `filename` tool parameter through the entire outbound delivery pipeline so that WhatsApp documents arrive with the user-specified file name instead of the hardcoded default "File"
- Adds `fileName` as an optional field to the gateway protocol schema, channel outbound context, and all intermediate delivery types
- Uses the caller-supplied name as an override, preserving the existing auto-detection from media URL as fallback
## Problem
When sending documents (PDFs, spreadsheets, etc.) via the `message` tool on WhatsApp, the `filename` parameter is accepted by the tool schema but never forwarded through the send pipeline. The Baileys library defaults the document name to "File" when none is provided, so every document arrives with a generic name regardless of what the user specified.
**Root cause:** The `filename` parameter was defined in the message tool schema (`message-tool.ts`) but the outbound delivery pipeline had no mechanism to carry it from the tool invocation through to `sendMessageWhatsApp`. The pipeline consists of multiple layers (action runner, send service, message dispatcher, delivery queue, channel handler, and finally the WhatsApp-specific sender), and none of them included a `fileName` field.
**Reproduction steps:**
1. Configure a WhatsApp channel and link an account
2. Use the message tool to send a document with an explicit filename:
```
message --to <recipient> --media /path/to/report.pdf --filename "Q4 Financial Report.pdf"
```
3. Observe that the recipient receives the document named "File" instead of "Q4 Financial Report.pdf"
## Solution
Added `fileName` as an optional string field at each layer of the outbound pipeline:
1. **`ChannelOutboundContext`** (`types.adapters.ts`) -- the type received by all channel plugin outbound adapters
2. **`ChannelHandlerParams` and `DeliverOutboundPayloadsCoreParams`** (`deliver.ts`) -- internal delivery pipeline types
3. **`createChannelOutboundContextBase`** (`deliver.ts`) -- propagates `fileName` from handler params to outbound context
4. **`MessageSendParams`** (`message.ts`) -- the params accepted by the core `sendMessage` function
5. **`executeSendAction`** (`outbound-send-service.ts`) -- bridges the action runner to `sendMessage`
6. **`handleSendAction`** (`message-action-runner.ts`) -- extracts `filename` from tool params via `readStringParam`
7. **`SendParamsSchema`** (`agent.ts`) -- gateway protocol validation schema (has `additionalProperties: false`)
8. **Gateway `send` handler** (`send.ts`) -- extracts `fileName` from the validated request and passes it to `deliverOutboundPayloads`
9. **WhatsApp outbound adapters** (both `extensions/whatsapp/src/channel.ts` and `src/channels/plugins/outbound/whatsapp.ts`) -- destructure `fileName` from context and forward to `sendMessageWhatsApp`
10. **`sendMessageWhatsApp`** (`outbound.ts`) -- accepts `fileName` in options and uses it as override: `options.fileName || media.fileName`
The override logic in `sendMessageWhatsApp` is:
```typescript
documentFileName = options.fileName || media.fileName;
```
This means the user-provided name takes priority. If not provided, the existing auto-detection from the media URL path continues to work as before. If neither is available, Baileys' internal default ("file") applies.
## Files Changed
| File | Change |
|------|--------|
| `src/channels/plugins/types.adapters.ts` | Added `fileName?: string` to `ChannelOutboundContext` |
| `src/infra/outbound/deliver.ts` | Added `fileName` to `ChannelHandlerParams`, `DeliverOutboundPayloadsCoreParams`, `createChannelOutboundContextBase`, and `createChannelHandler` call |
| `src/infra/outbound/message.ts` | Added `fileName` to `MessageSendParams`, threaded through both direct and gateway delivery paths |
| `src/infra/outbound/outbound-send-service.ts` | Added `fileName` to `executeSendAction` params, passed to `sendMessage` |
| `src/infra/outbound/message-action-runner.ts` | Extracts `filename` from tool params, passes to `executeSendAction` |
| `src/gateway/protocol/schema/agent.ts` | Added optional `fileName` to `SendParamsSchema` |
| `src/gateway/server-methods/send.ts` | Extracts `fileName` from gateway request, passes to `deliverOutboundPayloads` |
| `src/web/outbound.ts` | Added `fileName` to `sendMessageWhatsApp` options, uses as override for `documentFileName` |
| `extensions/whatsapp/src/channel.ts` | Passes `fileName` from context to `sendMessageWhatsApp` |
| `src/channels/plugins/outbound/whatsapp.ts` | Passes `fileName` from context to `sendMessageWhatsApp` |
## Test Plan
- [x] `pnpm tsgo` -- type checking passes with zero errors
- [x] `pnpm lint` -- linter passes with zero warnings
- [x] `pnpm format:check` -- formatting passes
- [x] `pnpm vitest run src/web/outbound.test.ts` -- 9 tests pass
- [x] `pnpm vitest run src/infra/outbound/outbound-send-service.test.ts` -- 3 tests pass
- [x] `pnpm vitest run src/gateway/server-methods/send.test.ts` -- 10 tests pass
- [x] `pnpm vitest run src/infra/outbound/message-action-runner.threading.test.ts` -- 7 tests pass
- [x] `pnpm vitest run src/channels/channels-misc.test.ts` -- 5 tests pass
- [ ] Manual verification: send a document via WhatsApp with `--filename "My Report.pdf"` and confirm the recipient sees the correct name
## Backward Compatibility
This change is fully backward compatible:
- `fileName` is optional at every layer; omitting it preserves existing behavior
- When not provided, `media.fileName` (auto-detected from URL/path) is still used as before
- The gateway schema change adds an optional field; existing clients that don't send `fileName` are unaffected
- No changes to the public tool schema (the `filename` parameter was already defined)
Fixes openclaw/openclaw#21838
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR successfully threads the `fileName` parameter through the entire WhatsApp document send pipeline, fixing the issue where documents were always sent with the default "File" name. The implementation adds `fileName` as an optional field at each layer (gateway schema, outbound context, delivery pipeline, and WhatsApp sender), with proper fallback to auto-detected filenames from media URLs when not provided.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk
- The change is a straightforward parameter threading exercise with proper optional field handling, existing test coverage, and no breaking changes. The implementation follows established patterns in the codebase and maintains backward compatibility.
- No files require special attention
<sub>Last reviewed commit: 07f5209</sub>
<!-- greptile_other_comments_section -->
<sub>(4/5) You can add custom instructions or style guidelines for the agent [here](https://app.greptile.com/review/github)!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#16785: fix(whatsapp): preserve document filenames in outbound Baileys mode
by SahilSahu731 · 2026-02-15
93.8%
#7458: fix: pass filename through to WhatsApp document sends (#7446)
by gavinbmoore · 2026-02-02
90.9%
#10889: fix: pass fileName through WhatsApp document send path
by DeveshParagiri · 2026-02-07
90.2%
#15650: fix(whatsapp): pass fileName to document sends instead of hardcodin...
by whoknowsmann · 2026-02-13
88.9%
#6566: fix: thread fileName through WhatsApp document send path
by giannisanni · 2026-02-01
88.3%
#9606: fix: pass fileName to WhatsApp document messages
by AytuncYildizli · 2026-02-05
87.3%
#16817: fix(whatsapp): infer extension-aware fallback filename for Baileys ...
by VintLin · 2026-02-15
85.5%
#23579: feat(whatsapp): add sender attribution to inbound message logs
by svan058 · 2026-02-22
78.1%
#8705: feat(whatsapp): add viewOnce support for ephemeral media
by ndohuu · 2026-02-04
77.5%
#21953: feat(whatsapp): sender prefix on BodyForAgent + contactNames config
by mactsk · 2026-02-20
76.4%