Skip to content

fix: normalize FormData body in proxy fetch for undici compatibility#879

Open
BingqingLyu wants to merge 1 commit into
mainfrom
fork-pr-48556-fix-proxy-fetch-formdata-compat
Open

fix: normalize FormData body in proxy fetch for undici compatibility#879
BingqingLyu wants to merge 1 commit into
mainfrom
fork-pr-48556-fix-proxy-fetch-formdata-compat

Conversation

@BingqingLyu

@BingqingLyu BingqingLyu commented Apr 27, 2026

Copy link
Copy Markdown
Owner

Summary

  • Problem: When HTTPS_PROXY is set, resolveProxyFetchFromEnv() and makeProxyFetch() wrap undici.fetch as a drop-in for globalThis.fetch. But in Node 22+, globalThis.FormData and undici.FormData are different classes — undici doesn't recognize the global one and serializes it as "[object FormData]" instead of multipart/form-data.
  • Why it matters: Audio transcription and batch file uploads silently fail for all users behind an HTTP proxy. Voice messages are never transcribed.
  • What changed: Added normalizeInitForUndici() in proxy-fetch.ts that converts globalThis.FormData to undici's FormData before dispatch. Applied to both makeProxyFetch() and resolveProxyFetchFromEnv().
  • Scope boundary: No changes to callers (audio.ts, batch-upload.ts) — they correctly use new FormData(). The proxy wrapper was the broken contract.

Change Type

  • Bug fix

Scope

  • Gateway / orchestration
  • Integrations

Linked Issue

Closes openclaw#48554

User-visible / Behavior Changes

Audio transcription via Telegram/Discord voice messages now works correctly when HTTPS_PROXY or HTTP_PROXY is set.

Security Impact

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No — same requests, just properly formatted
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Reproduction + Verification

Environment

  • OS: Ubuntu 24.04 (Docker)
  • Runtime: Node.js 24.14.0, undici 7.24.1
  • Integration: Telegram voice messages
  • Config: HTTPS_PROXY set, tools.media.audio configured with OpenAI-compatible provider (Speaches/local Whisper)

Steps

  1. Set HTTPS_PROXY in OpenClaw container
  2. Configure tools.media.audio with a local Whisper endpoint
  3. Send a Telegram voice message

Expected

Audio transcribed, agent responds to transcript

Actual (before fix)

Request body is literal string [object FormData], Content-Type is text/plain;charset=UTF-8. Transcription fails with HTTP 422 (missing file and model fields).

Evidence

  • 4 new unit tests (FormData conversion for both wrappers, non-FormData passthrough, no double-conversion)
  • End-to-end verified: Telegram voice → Speaches transcription working after fix
  • pnpm check passes (build + format + lint + full test suite)

Human Verification

  • Reproduced bug inside OpenClaw container with debug HTTP server capturing raw request
  • Confirmed globalThis.FormData !== require("undici").FormData in Node 24
  • Verified fix resolves the issue end-to-end via Telegram voice messages
  • Verified non-FormData bodies pass through unchanged
  • Verified undici FormData instances are not re-converted

AI Assistance

  • AI-assisted (Claude Code)
  • Fully tested
  • I understand what the code does

Compatibility / Migration

  • Backward compatible: Yes
  • Config/env changes required: No
  • Migration needed: No

Failure Recovery

  • Revert this commit
  • No config changes needed

Risks and Mitigations

  • Risk: FormData entry iteration might miss edge cases (e.g., multiple values for same key)
    • Mitigation: FormData.entries() returns all entries including duplicates; append() preserves them
  • Risk: globalThis.FormData might not exist in some environments
    • Mitigation: Guard checks typeof GlobalFormData === "function" before instanceof

In Node 22+, globalThis.FormData and undici's FormData are different
classes. When HTTPS_PROXY is set, resolveProxyFetchFromEnv() and
makeProxyFetch() wrap undici.fetch as a drop-in for globalThis.fetch.
But undici.fetch doesn't recognize globalThis.FormData — it calls
.toString() on it, sending "[object FormData]" as text/plain instead
of proper multipart/form-data.

This breaks audio transcription (Telegram/Discord voice messages) and
batch file uploads for any user behind an HTTP proxy.

Add normalizeInitForUndici() that converts globalThis.FormData bodies
to undici's FormData before dispatch. The helper is a no-op when the
classes are identical or when the body is not a FormData instance.

Closes openclaw#48554
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.

fix: audio transcription sends [object FormData] when HTTP proxy is configured (Node 22+ FormData/undici mismatch)

2 participants