fix(line): retry inbound media download while LINE reports 202 (still preparing)#108351
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 15, 2026, 11:40 PM ET / July 16, 2026, 03:40 UTC. Summary PR surface: Source +35, Tests +56. Total +91 across 2 files. Reproducibility: yes. The contributor reproduced the current single-request path against a real LINE channel, observed HTTP 202 with an empty body, and showed the patched runtime saving the complete media after retry. Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the plugin-local status-aware retry after refreshing it against current main and retaining both the immediate-success and bounded-exhaustion regression tests. Do we have a high-confidence way to reproduce the issue? Yes. The contributor reproduced the current single-request path against a real LINE channel, observed HTTP 202 with an empty body, and showed the patched runtime saving the complete media after retry. Is this the best way to solve the issue? Yes. Inspecting the documented content-endpoint status at the LINE plugin boundary is narrower and more reliable than adding a new core contract or relying on the transcoding-status endpoint. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 957cc81175a3. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +35, Tests +56. Total +91 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
b581ef7 to
93d0a8a
Compare
93d0a8a to
39f0a34
Compare
LINE answers the content endpoint with 202 and an empty body for a short window while it prepares inbound media. The previous single-shot download saved that empty body as a 0-byte file, silently dropping the user's media. Poll the content endpoint with capped exponential backoff until it stops returning 202, releasing each empty response before retrying, and throw a visible error if it never becomes ready.
39f0a34 to
e7dd96f
Compare
|
Merged via squash.
|
… preparing) (openclaw#108351) * fix(line): retry inbound media download while LINE reports 202 LINE answers the content endpoint with 202 and an empty body for a short window while it prepares inbound media. The previous single-shot download saved that empty body as a 0-byte file, silently dropping the user's media. Poll the content endpoint with capped exponential backoff until it stops returning 202, releasing each empty response before retrying, and throw a visible error if it never becomes ready. * fix(line): clean up media retry responses * fix(line): use plugin runtime fetch seam --------- Co-authored-by: Peter Steinberger <[email protected]>
What Problem This Solves
LINE can answer
GET /v2/bot/message/{messageId}/contentwith202 Acceptedwhile inbound media is still being prepared. OpenClaw previously passed that empty response to the media store and could persist a 0-byte attachment. This fixes the remaining download half of #96163; #96403 handled filename/classification.Why This Is the Right Fix
LINE documents
202on the content endpoint as the not-ready signal. The plugin now polls that endpoint directly, retries only202, and uses capped exponential backoff: 500ms, 1s, 2s, 4s, 4s; six total attempts. A 15-second readiness deadline aborts a hung request or backoff. Redirects are rejected so the bearer token cannot be forwarded to another origin.The plugin SDK runtime-fetch path is intentional.
@line/bot-sdk11.1.0 wraps the response reader in a NodeReadablewithout a destroy hook, so destroying that wrapper does not cancel the underlying web stream. This implementation uses the dispatcher-aware plugin SDK fetch wrapper, cancels every discarded202or error response, converts the accepted200body withReadable.fromWeb, and destroys/awaits that stream if the media store rejects it.No plugin SDK API changes. The exported
downloadLineMediasignature and plugin SDK imports remain unchanged; the generated SDK baseline and plugin-boundary checks pass.User Impact
Media sent during LINE's preparation window is retried and delivered instead of becoming a 0-byte attachment. Exhausted, failed, or hung downloads surface through the existing media-unavailable path instead of leaking response bodies or silently saving empty content.
Evidence
Contributor live LINE evidence, gathered with the production download path (message IDs redacted):
Maintainer proof (live credentials unavailable; mocked recorded sequences accepted for this path):
Regression coverage includes
202 -> 202 -> 200, six exhausted202responses, non-202errors, a hung request, downstream media-store rejection, exact cancellation of every unconsumed body, encoded message IDs, and the full backoff schedule.Official contract: https://developers.line.biz/en/reference/messaging-api/#get-content and https://developers.line.biz/en/docs/messaging-api/retrying-api-request/
Fixes #96163