fix #92246: QQ Bot C2C: Media sending (image/file) returns 400 Bad Request#92747
fix #92246: QQ Bot C2C: Media sending (image/file) returns 400 Bad Request#92747zhangguiping-xydt wants to merge 5 commits into
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 13, 2026, 2:11 PM ET / 18:11 UTC. Summary PR surface: Source +21, Tests +243. Total +264 across 5 files. Reproducibility: no. independent high-confidence live reproduction by this review. Source inspection plus the linked issue logs show the old one-shot C2C media upload path, and the PR supplies redacted live after-fix QQBot logs. Review metrics: 1 noteworthy metric.
Stored data model Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Next step before merge
Security Review detailsBest possible solution: Land a focused QQBot plugin repair after maintainer review and required checks, keeping the fix inside the existing sender/upload boundary without adding config or channel API surface. Do we have a high-confidence way to reproduce the issue? No independent high-confidence live reproduction by this review. Source inspection plus the linked issue logs show the old one-shot C2C media upload path, and the PR supplies redacted live after-fix QQBot logs. Is this the best way to solve the issue? Yes; the updated patch uses the existing QQBot sender dispatch and chunked uploader rather than adding a new tool, config option, or API, and it now covers the previously missed source shapes. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against ca2410ab072e. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +21, Tests +243. Total +264 across 5 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
|
|
Additional QQ client UI proof for #92246: |
|
closing this because the broad C2C routing change is not supported by the current QQ contract or live proof. QQ's official rich-media contract documents This PR instead forces every C2C image/file source through the chunked upload path based only on generic HTTP 400 reports, without the rejected response body/error code or a live before/after proof. That can move all small C2C media onto a different quota/failure surface while bypassing the documented one-shot path. The adapter error-propagation improvement is useful, but it should be a separate bounded fix; the upload-route change is too speculative to land. |

Summary
/filesfile_dataupload contract, while QQ's C2C image/file path accepts these bytes through the chunked upload contract; the channel adapter also normalized lower-layer QQ upload failures into empty successful receipts.upload_prepare -> COS PUT -> upload_part_finish -> /fileschunked contract before/messages, so the failing upload contract is no longer used for C2C image/file delivery. The adapter also preserves the delivery invariant that a successful generic send must include a QQ platform message id.Linked context
Closes #92246
Related #92246
Was this requested by a maintainer or owner? No maintainer request found; this follows the issue report and reproduces the reported C2C image/file failure path.
Real behavior proof (required for external PRs)
Behavior or issue addressed: QQBot C2C image/file sends through the generic message path returned QQ API HTTP 400 during media upload while OpenClaw could still surface an empty successful receipt.
Real environment tested: a live QQBot app and a real C2C private-chat target captured from
C2C_MESSAGE_CREATE; credentials, target openid, token,file_info, and COS presigned URLs were redacted.Exact steps or command run after this patch:
Evidence after fix (screenshot, recording, terminal capture, console output, redacted runtime log, linked artifact, or copied live output):
Live QQBot C2C send proof, redacted:
Observed result after fix: both live C2C URL image and URL file sends completed the chunked upload sequence and returned non-empty QQ message ids from
/messages.What was not tested: group media sends, voice/video media sends, and QQ client UI screenshot capture from this environment.
Proof limitations or environment constraints: I could verify the live QQBot API path and returned message ids, but I do not have direct access to the tester's QQ client UI in this coding environment, so I did not attach a QQ client screenshot here. The non-URL C2C byte-source extension is covered by focused sender regressions because the same chunked uploader contract is exercised by the live URL proof.
Before evidence (optional but encouraged): the issue report shows the previous C2C media upload path failing with
API Error [/v2/users/{openid}/files]: Bad Request/ HTTP 400. The new sender regression failed before the expanded implementation for C2C base64, buffer, and localPath image sources because each still calledMediaApi.uploadMediaone-shotfile_data; the adapter regression also failed before the implementation because a lower-layer QQ upload error could resolve as an empty successful receipt.Tests and validation
Which commands did you run?
What regression coverage was added or updated?
ChunkedMediaApi.uploadChunked, notMediaApi.uploadMediaone-shotfile_data.ChunkedMediaApi.uploadChunkedinstead of the one-shotfile_datapath.What failed before this fix, if known?
file_dataupload path.file_dataupload path.check-lintandcheck-additional-extension-bundledfailed on the no-opString(...)conversion in the QQBot channel adapter; the conversion was removed and both corresponding local lint commands now pass.If no test was added, why not? Regression tests were added.
Risk checklist
Did user-visible behavior change? (
Yes/No)Yes. C2C image/file sends should now succeed via QQ's chunked upload path for URL/base64/buffer/localPath byte sources, and failed QQ media sends now reject instead of returning an empty success.
Did config, environment, or migration behavior change? (
Yes/No)No.
Did security, auth, secrets, network, or tool execution behavior change? (
Yes/No)Yes, network behavior changes narrowly for QQBot C2C image/file sends: bytes now use the existing chunked upload flow. Remote URL fetches still go through the existing SSRF-guarded downloader, local files still go through the existing safe-open path, and this PR does not expose secrets or add config.
What is the highest-risk area?
The highest-risk area is QQBot media upload routing for C2C image/file byte sources.
How is that risk mitigated?
The change is scoped to C2C image/file sources, reuses the existing chunked upload implementation, preserves guarded URL download and safe local-file handling, adds focused regression tests for URL/base64/buffer/localPath sources, passes typecheck/lint, and was verified against a live QQBot C2C target for URL image and URL file delivery.
Current review state
What is the next action?
Ready for maintainer review after updated CI settles.
What is still waiting on author, maintainer, CI, or external proof?
Waiting on updated CI and maintainer review. A QQ client UI screenshot can be added later if the tester shares one, but live QQBot API proof for image and file delivery is included above.
Which bot or reviewer comments were addressed?
status: waiting on authorlabel): author-side follow-up was completed in this update; the label should be cleared by maintainer/bot once updated checks/review state are accepted.file_data.String(...)conversion and re-running the two corresponding local lint commands successfully.sender.tssource branch): fixed in the unified sender dispatch; new regressions cover URL/base64/buffer/localPath C2C image/file routing through the chunked uploader.