fix: media upload — statusless errors, remote content upload, controlled validation #13
No reviewers
Labels
No labels
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
waiting-on-julian
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jlxq0/listmonk-mcp!13
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/t_3181e5c4-upload-media"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fleet tasks t_3181e5c4 and t_2dc46427 (both Honoka's cards). This is the one PR for both: t_2dc46427's work fast-forwards this branch from
b7e7451. Do not merge without Julian's authorization.Fork classification: upstreamable. The error mapping, upload validation and listing fix are generic. The AGENTS.md entries are fork-local.
Root cause (reproduced, not assumed)
Reproduced against a real Listmonk v6.1.0 run locally in Docker, through the real MCP interface (streamable HTTP):
file_pathis read on the server's filesystem, and the fondue pod mounts only/tmp. The client raisedListmonkAPIError(status_code=None)("File not found"), and the error mapper's400 <= Noneraised inside theexcepthandler. That produced exactly'<=' not supported between instances of 'int' and 'NoneType'.b7e7451alone fixes the crash and surfaces "File not found", but a remote caller still can't upload.c4e74b6and2a67353make remote upload work.Changes
b7e7451: errors with no HTTP status map to an APIError carrying the client's message.c4e74b6, remote upload:upload_media_filetakes optionalcontent_base64+filename.file_pathandtitleare unchanged for existing callers.c4e74b6, controlled refusals: every refusal is aMediaValidationErrorraised before any request and reported asValidationError. That covers missing, inaccessible, not a regular file, unreadable, empty, overmax_upload_bytes, unsupported type, bytes that don't match the type, bad base64, and afilenamecontaining a path.c4e74b6, size limit:max_upload_bytesdefaults to Listmonk's own 5000 KB.Nonemeans no limit and is never compared. The boundary is inclusive.c4e74b6, listing: a missing,nullor non-numeric media size shows asunknowninstead of breaking.2a67353, review round 1:O_NONBLOCKand checked on the descriptor, so it can't be swapped before the read and a FIFO can't block;e034a69: an AGENTS.md wording fix (the exact raw-base64 threshold).file_pathis server-local;Nonelimit;title, suffixes duplicate names, has nometa.size, returnsdata.results, and has noPUT /api/media;Tests (
tests/test_upload_media.py, 45 tests)d39ff97: 35 fail, 6 of them with the original TypeError.b7e7451alone: 31 fail.AC3: real upload, list readback and GET (local Listmonk v6.1.0, not live)
Sent as
content_base64through the MCP client, so the server never saw the path:get_media_listreadbackcfa1ddbd…http://127.0.0.1:53659/uploads/t2dc-r2.svgID: 6 | t2dc-r2.svg, same URLimage/svg+xml, 129 B, identical7e54100b…http://127.0.0.1:53659/uploads/t2dc-r2.jpgID: 7, same URLimage/jpeg, 225 B, identicalfile_pathroute: also uploads; Listmonk suffixed the duplicate name (t2dc-r2_eGxQuj.svg), and the tool reports that name..txt, and fake SVG came back asValidationErrorand created no media.Review (independent, different models)
2a67353.2a67353: all four FIXED, AC1–AC3 PASS. AC4 failed only on the AGENTS.md threshold wording, fixed ine034a69.2a67353at 22:02 +08: APPROVE, all criteria met.e034a69is that one AGENTS.md line,2a67353..e034a69.Deployment (not done here; Clark)
fondue runs
listmonk-mcp:v0.2.1, which has neither fix, so Honoka's mounted tool stays on the old behaviour until a release and Clark's rollout. After rollout, the live check is: Honoka uploads a fixture viacontent_base64, finds the exact entry inget_media_list, and GETs its URL. That has not been done.Found, not fixed
rename_mediacallsPUT /api/media/:id, which Listmonk v6.1.0 doesn't have, so it can't work. That needs its own decision.🤖 Generated with Claude Code
https://claude.ai/code/session_01MjY9WoGT5SGnSCrYDVdiWJ
upload_media_file read file_path on the server's own filesystem. Over streamable-HTTP the server is a pod, so a caller's local file is "File not found" there, which used to surface as the int-vs-None TypeError. Reproduced against a real Listmonk v6.1.0: the same original SVG uploads when the server can read it. - content_base64 + filename upload bytes from a caller on another host; file_path keeps working unchanged - every refusal is a MediaValidationError raised before any request and reported as ValidationError: missing, inaccessible, not a regular file, unreadable, empty, over max_upload_bytes, unsupported type, signature mismatch, bad base64, filename with a path - max_upload_bytes defaults to Listmonk's 5000 KB; None means no limit and is never compared; the boundary is inclusive - media listings tolerate absent/null/non-numeric size ("unknown") Builds onb7e7451(statusless error mapping) in PR #13. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjY9WoGT5SGnSCrYDVdiWJfix: map statusless ListmonkAPIError instead of crashingto fix: media upload — statusless errors, remote content upload, controlled validationThird independent review (Codex) on exact head
e034a69: APPROVE.2a67353e034a69touches only AGENTS.md (one line).2a67353, so the round-2 AC1-AC3 PASS verdicts carry over.CI on
e034a69: CI / python and CI / docker both green. Not merged: this card came from Honoka and needs Julian's authorization.