Skip to content

Fix deploy-time profile sync: Telegram rejects empty multipart requests - #73

Merged
escalopa merged 1 commit into
mainfrom
fix/profile-sync-empty-multipart
Aug 3, 2026
Merged

Fix deploy-time profile sync: Telegram rejects empty multipart requests#73
escalopa merged 1 commit into
mainfrom
fix/profile-sync-empty-multipart

Conversation

@escalopa

@escalopa escalopa commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Symptom

The last three testing deploys ended with the warning "Telegram profile synchronization remained temporarily unavailable after retries". It looked transient; it wasn't.

Root cause (verified with a raw-transport probe)

  • The go-telegram/bot library encodes every API call as multipart/form-data — including parameterless methods (getMyName, getMe, getChatMenuButton), which it sends with a multipart Content-Type and a zero-byte body.
  • Telegram now answers those with a bodyless HTTP 400, which the library surfaces as "unexpected end of JSON input" — classified as transient, retried 3×, always failing.
  • The same requests succeed as application/json {} (verified with curl); the latest library (v1.23.0) still has the bug.

Fix

A RoundTripper in the botprofile adapter rewrites a multipart request whose body is empty (or only a closing boundary) into an application/json {} body. Requests carrying any field — including the avatar upload — pass through untouched (≤128-byte guard). Wired only into cmd/botprofile; the runtime services never call parameterless methods (all use WithSkipGetMe, every call has fields).

Verified live

Ran cmd/botprofile against the testing bot with this fix:

  • PROFILE_SYNC_STATUS=synchronized (first success in 3 deploys)
  • getMyCommands now lists all 12 commands including /city
  • webhook unchanged, pending: 0, last_error: none

So the testing bot is already fully synced; merging this makes future deploys stop tripping over it.

Tests

Transport unit tests: bodyless multipart → JSON, closing-boundary-only multipart → JSON, multipart with fields → untouched. make check green. Triage row in operations.md updated.

🤖 Generated with Claude Code

Telegram now answers a bodyless HTTP 400 when a multipart/form-data
request arrives with no fields. The bot library encodes parameterless
methods (getMyName, getMe, getChatMenuButton) exactly that way — a
multipart Content-Type with a zero-byte body — so profile sync failed
deterministically on its first read call in the last three deploys,
misreported as transient. A RoundTripper on cmd/botprofile's HTTP
client rewrites the empty envelope into an application/json {} body,
which Telegram accepts; any request carrying fields passes through
untouched. Runtime services are unaffected: every call they make
carries at least one field.

Verified live against the testing bot: sync now reports
PROFILE_SYNC_STATUS=synchronized and /city appears in the command menu.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@escalopa
escalopa merged commit 45ca9b9 into main Aug 3, 2026
4 checks passed
@escalopa
escalopa deleted the fix/profile-sync-empty-multipart branch August 16, 2026 00:41
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