MEDIA: download links 404 when token is wrapped in markdown emphasis or trailing punctuation
#6890 aperta il 10 ago 2026
Metriche repository
- Star
- (17.224 stelle)
- Metriche merge PR
- (Merge medio 14h 31m) (314 PR mergiate in 30 g)
Descrizione
Summary
Chat MEDIA:<path> download links 404 when the model wraps the token in markdown (bold/italics/code) or glues on sentence punctuation. The file on disk is fine — the frontend builds a URL that includes the trailing markup.
Repro
Agent reply containing:
Done. Here's the sheet.
**MEDIA:/data/workspace/100-camera-cctv-turnkey-comparison.xlsx**
Rendered link label becomes 📎 100-camera-cctv-turnkey-comparison.xlsx** and the href is:
api/media?path=%2Fdata%2Fworkspace%2F100-camera-cctv-turnkey-comparison.xlsx%2A%2A&download=1
GET /api/media with the clean path → 200 + xlsx bytes.
Same request with the trailing ** → 404 {"error":"not found"}.
Root cause
In static/ui.js renderMd() (and the matching streaming path in static/messages.js), MEDIA tokens are stashed before markdown runs:
s=s.replace(/MEDIA:([^\s\)\]]+)/g,(_,raw_ref)=>{
media_stash.push(raw_ref);
return '\x00D'+(media_stash.length-1)+'\x00';
});
[^\s\)\]]+ does not stop at *, `, _, or sentence punctuation, so closing ** / backticks / a trailing . are captured into the path. _inlineMediaHtmlForRef then encodeURIComponents that polluted ref into /api/media.
The plain-URL autolink pass already strips trailing [.,;:!?)] — MEDIA does not.
Scale
On one production device: 106 / 317 MEDIA tokens (~33%) had trailing junk captured this way (mostly trailing ., then backticks, then **).
Proposed fix
Mirror what the Hermes iOS client already does in TranscriptMediaParser (see nesquena/hermes-webui consumers / Performance Hub iOS TranscriptMedia.swift):
- After capturing the ref (or inside
_inlineMediaHtmlForRef), strip trailing sentence punctuation[.,;:!?]+. - If the token is immediately preceded by a markdown emphasis/code delimiter (
***/___/**/__/*/_/`), also strip the matching closing delimiter from the end of the ref so it stays outside the path. - Apply the same normalisation in the streaming
messages.jsMEDIA path (it shares_inlineMediaHtmlForReftoday, so fixing that helper once covers both settled + live rendering + historical transcripts on re-paint).
Happy to send a PR if useful — smallest change is a _normalizeMediaRef(ref) helper called from _inlineMediaHtmlForRef (and optionally from the stash callback so leftover opening ** can still bold-wrap the link cleanly).
Environment
Observed on hermes-webui 0.52.151 (ghcr.io/nesquena/hermes-webui@sha256:75efbb5f…); master static/ui.js still has the same stash regex as of this report.