Repository navigation
fix(server): absolute file:// URLs never resolve under --media-path #1612
Description
Problem
--media-path does permit file:// reads, but only for references interpreted as relative to the root. An absolute file:// URL, the form every other tool accepts and the form scripts/bench_embeddings.py:17 generates, can never resolve. resolve_media_file_in (src/server/media_root.rs:209-226) strips the file:// prefix, strips leading separators via relative_component, then joins the remainder onto the root, so file://<abs-repo>/tests/fixtures/test_image.png under --media-path <abs-repo>/tests/fixtures is probed at <root>/Users/.../tests/fixtures/test_image.png. That does not exist, and the client is told file does not exist or cannot be opened: followed by the path it sent, naming a file that is present and readable. The path actually probed is never disclosed, so the error is undiagnosable from the outside.
The concatenation itself is deliberate llama-server b10621 compatibility, pinned by an_absolute_looking_path_is_concatenated_not_joined (src/server/media_root_tests.rs:72-92). The defect is that nothing states the rule: --help (src/cli/multimodal_compat_args.rs:199-204) says only "Directory that local file:// media URLs are resolved against", and docs/llama-server-compat.md:485 says "the request's path is resolved against the configured directory".
Verified on this checkout
Server started with --media-path "$PWD/tests/fixtures" on nvidia/llama-nemotron-embed-vl-1b-v2, then one POST /v1/embeddings per row with {"input":[{"type":"image_url","image_url":{"url":URL}}]}.
| URL in the request | Result |
|---|---|
file://<abs-repo>/tests/fixtures/test_image.png |
HTTP 400, file does not exist or cannot be opened |
file://test_image.png |
HTTP 200 |
test_image.png |
HTTP 200 |
file:///test_image.png |
HTTP 200 |
data:image/png;base64,... |
HTTP 200 |
The three accepted file:// forms return a 2048-dim vector bit-identical to the data: URI form (max abs diff 0.0), so the read and decode path is healthy and only the absolute form fails. Containment still holds: file://../../etc/passwd is refused with file path is not allowed. Environment: mlxcel 0.6.0 at b2ff1eee, macOS 26.6.2 (25G83), M5 Max 128GB, --features metal,accelerate, MLX pin 9a795735.
Proposed solution
Keep the upstream concatenation as the primary resolution so b10621 compatibility and every existing containment test hold unchanged, and add an absolute-path fallback in resolve_media_file_in: when the concatenated candidate does not resolve and raw is absolute, canonicalize raw itself and accept it only if it passes the same canonical.starts_with(root) check already there. This only converts previous errors into acceptances, never the reverse, and adds no second containment path. An absolute path outside the root must surface as MediaPathError::Escape, not Unresolvable.
Then make the failure self-explaining. MediaPathError::Unresolvable is asserted only with contains (src/server/media_root_tests.rs:236-240), unlike NO_MEDIA_ROOT_MESSAGE which is pinned character for character (:246-253), so its message may carry a suffix saying paths resolve relative to --media-path. Log the probed candidate at debug rather than returning it, so the root is not disclosed to clients. State the rule in the --help text and docs/llama-server-compat.md:483-496.
Out of scope: the NoRoot wording, and the video path in src/server/media.rs. scripts/bench_embeddings.py can stay on the data: URI, which needs no allow-list, but its stale docstring at :106 and comment at :171 claim file:// is refused outright and must be corrected.
Acceptance criteria
- An absolute
file://URL naming a file inside the root is accepted by/v1/embeddingsand/v1/rerankand returns a vector identical to thedata:URI form for the same image. (PR fix(server): resolve absolute file:// URLs under --media-path #1620. The fix is in the shared resolver, whose sole caller serves all four routes; verified end to end on/v1/chat/completionswithmodels/mlx/qwen2.5-vl-3b-instruct, becausenvidia/llama-nemotron-embed-vl-1b-v2is not on the host.) - An absolute
file://URL naming a file outside the root is refused as an escape, and the existing symlink-escape and traversal tests still pass unchanged. (PR fix(server): resolve absolute file:// URLs under --media-path #1620.) -
file://test_image.png,test_image.png, andfile:///test_image.pngstill resolve. (PR fix(server): resolve absolute file:// URLs under --media-path #1620.) - The unresolvable error tells the operator that paths are resolved relative to
--media-path, and the probed candidate appears in the server log but not in the response. (PR fix(server): resolve absolute file:// URLs under --media-path #1620, atdebuglevel.) -
--helpanddocs/llama-server-compat.mdstate the resolution rule and show an accepted absolute and an accepted relative form. (PR fix(server): resolve absolute file:// URLs under --media-path #1620. The new divergence is also recorded on the--media-pathmanifest entry's checkeddivergencelist.) - The "Open finding" section at
docs/benchmark_results/embeddings-rerank-m5max-2026-09-04.md:79-88is corrected. (Premise corrected: that document exists only onbench/0.7.0-refresh(PR bench: refresh the 0.7.0 sweeps and fix the dtype, rotary and rope defects they exposed #1617), and the section had already been rewritten there as "Why the image cells use a data URI" with an accurate description. The remaining work was the opposite, updating it for the fix, and is commitc3a999b1eon that branch, NOT onmain.) - The change lands in the live request path used by the embeddings, rerank, chat, and responses media handlers, not in a standalone helper. (PR fix(server): resolve absolute file:// URLs under --media-path #1620, inside
resolve_media_file_in.)
Verification
cargo test --release media_root
cargo clippy --all-targets -- -D warnings
cargo fmt --checkManual, against a real checkpoint:
./target/release/mlxcel-server -m <store>/nvidia/llama-nemotron-embed-vl-1b-v2 --host 127.0.0.1 --port 18094 --media-path "$PWD/tests/fixtures" &
curl -s -X POST http://127.0.0.1:18094/v1/embeddings -H 'Content-Type: application/json' \
-d "{\"input\":[{\"type\":\"image_url\",\"image_url\":{\"url\":\"file://$PWD/tests/fixtures/test_image.png\"}}]}"A pass is HTTP 200 with a 2048-dim embedding equal to the one the data: URI form returns for tests/fixtures/test_image.png.
Origin
Introduced with the media confinement in #1451 (229d10e0, merged 2026-08-28). The GB10 embedding pass (benchmarks/cuda_gb10_embeddings_2026-08-26.csv) predates it by two days, which is why its image cells passed. On the 2026-09-04 M5 Max run all five image cells returned HTTP 400.
Activity
docs(benchmarks): update the media-path note for the #1612 fix
inureyes commentedon Sep 4, 2026
Implemented in PR #1620 (
fix/issue-1612-absolute-media-path). Two of this issue's premises pointed at PR #1617's branch rather thanmainand needed correcting first, and one acceptance criterion therefore lands on a different branch than the rest.Corrected premises.
docs/benchmark_results/embeddings-rerank-m5max-2026-09-04.mddoes not exist onmain; it exists only onbench/0.7.0-refresh(PR #1617). Its "Open finding" criterion was also already satisfied there before this work started: the section is titled "Why the image cells use a data URI" and already described the relative-only rule accurately. The real remaining edit was the opposite of what this issue asked, because the fix makes that description stale. Andscripts/bench_embeddings.pydoes not claimfile://is refused outright at:106and:171. Onmainit buildsf"file://{IMAGE}"from an absoluteIMAGE, which is exactly the failing form; onbench/0.7.0-refreshit already sends a base64data:URI through an_image_data_uri()helper. Either way the substance of the issue held.Satisfied on
mainby PR #1620: the absolute form inside the root is accepted through the shared resolver (sole callerread_confined_bytes_with_limitinsrc/server/media.rs:1437, so embeddings, rerank, chat and responses all get it at once); an absolute form outside the root is anEscape, with the symlink-escape and traversal tests unchanged;file://test_image.png,test_image.pngandfile:///test_image.pngstill resolve; the unresolvable error states that paths resolve relative to--media-pathwhile the probed candidate goes only to thedebuglog;--helpanddocs/llama-server-compat.mdstate the rule with an accepted absolute and an accepted relative form; the divergence is recorded on the--media-pathmanifest entry's checkeddivergencelist, not innotesprose.Satisfied on
bench/0.7.0-refresh, not onmain: the criterion coveringdocs/benchmark_results/embeddings-rerank-m5max-2026-09-04.md. That document lives only on PR #1617, which is deliberately staying open until the M1 Ultra, M3 Ultra and GB10 sweeps land in it, so its update is commitc3a999b1eon that branch. PR #1620 can merge and close this issue while that part is still unmerged.Deferred: the
_image_data_uri()docstring inscripts/bench_embeddings.py, which describes the pre-fix rule. The helper exists only onbench/0.7.0-refreshand that file is one of the files PR #1617 already rewrites, so correcting it there would collide with the open benchmark PR's own content. It is still right about why the harness sends a data URI, and it changes no measurement.Verified end to end.
nvidia/llama-nemotron-embed-vl-1b-v2is not on this host, so the check ran againstmodels/mlx/qwen2.5-vl-3b-instructon/v1/chat/completions, which reaches the identicalresolve_media_filefunnel, with--media-path <repo>/tests/fixtures, once with a binary built frommainand once with the branch. The chat route answers400 image resolution cardinality mismatchwhen an image part is dropped, so HTTP status separates an accepted image from a refused one.file://<repo>/tests/fixtures/test_image.pngfile does not exist or cannot be openedfile://test_image.pngtest_image.pngfile:///test_image.pngfile://<outside>/outside_image.pngfile does not exist or cannot be openedfile path escapes the --media-path rootfile:///etc/passwdfile does not exist or cannot be openedfile path escapes the --media-path rootfile://absent.pngfile does not exist or cannot be opened: absent.pngfile does not exist or cannot be opened: absent.png (paths are resolved relative to the --media-path root)On the last row the server logged
DEBUG mlxcel::server::media_root: local media reference did not resolve under the --media-path root reference="absent.png" probed=<repo>/tests/fixtures/absent.png. The probed candidate appears there and nowhere in the response.docs(bench): refresh the file:// notes in bench_embeddings.py for #1612
fix(server): resolve absolute file:// URLs under --media-path (lablup…
perf(cuda): convert bf16 weights to f16 at load on pre-Ampere GPUs (#…
perf(cuda/quant): add a Volta tensor-core arm to qmm_naive (#1678)
fix(bench): detect the host and inline media in bench_embeddings
docs(benchmarks): refresh M5 Max results for 0.6.0
fix(server): stop disclosing internal DNS in media URL errors (#2649)