Skip to content

Gallery reads reject an output:-prefixed name with a misleading "path does not exist"; list_gallery can't show a take's duration #356

Closed

Description

@dkackman

tool/endpoint: get_gallery_metadata (and every gallery read that resolves a name through _output_file: get_output_audio, get_output_frames, get_output_image, download_output, delete_output); list_gallery
repro:

  1. Run any audio template (e.g. templates/minimax/music/... or templates/generate-speech). list_gallery(limit=5) reports name: <folder>/<run>/final/<file>.mp3.
  2. get_gallery_metadata(name="output:<that name>"), spelled the way a workflow argument references an output.
  3. Run a Music 3 template twice so both runs write the same basename (e.g. MiniMaxMusic-balanced.1-0.0.mp3), then list_gallery().
    expected: (2) The output: prefix is accepted and stripped, as asset: already is on this endpoint, or it's refused with "drop the 'output:' prefix - gallery tools take the bare name". (3) The listing is enough to choose between the two takes without one metadata call per candidate.
    actual: (2) Unknown file: output:<name> - path does not exist: the prefix is joined into the path, and the message suggests the file is gone. In the field this nearly led an agent to conclude a score roll had been deleted. (3) Entries now carry run_id/version (Feat/run versions #336) but no duration. The two takes were 75.08 s (keep) and 42.25 s (reject), and size and mtime both pointed the wrong way: the later run was the short one, and the files were 808 KB at 42 s vs 1389 KB at 75 s, a bitrate difference, not a length one.
    code: dw/server/app.py:2503-2533 _output_file(name, root) does validate_path(os.path.join(root, name), ...) with no reference-prefix handling. The InvalidInputError branch produces "path does not exist". app.py:2913-2921 gallery_metadata special-cases only is_asset_reference(name). app.py:2719-2779 _gallery_entries builds entries with name/folder/subfolder/run_id/version/url/kind/size/mtime/label and no media fields.
    proposed fix:
  4. In _output_file (so every gallery read benefits), strip one leading output: before joining. Or, if the maintainers prefer a strict surface, raise a 404 whose detail says "names here are bare gallery names - drop the 'output:' prefix". Keep the traversal/containment checks on the stripped name.
  5. Add an opt-in media=true to list_gallery (/api/gallery?media=true) that adds duration_seconds for audio/video entries, using the probe get_gallery_metadata already uses (dw/media_info.probe_media), bounded by limit. Default listing unchanged.
  6. Tests: a prefixed name resolves (or gets the named refusal) on metadata and delete; a traversal attempt via output:../.. is still refused; media=true returns duration for a wav and a mp4.
    source: dw-findings-ledger DW-27 (T#6, A#15, A#16), triaged 2026-09-22 against develop f11ac2a

Filed from Don's dw findings ledger (dw-findings-ledger.html, DW-27). First-pass triage for applicability and value against open/closed issues and develop @ f11ac2a by claude-opus-5-5 via anthropic (interactive session with Don; code-checking subagents inherited that model).

Activity

  1. dkackman commented on Sep 22, 2026

    @dkackman
    OwnerAuthor

    triage: work — strip a leading output: in _output_file (dw/server/app.py) and add duration to gallery entries. (claude-opus-5-5 via anthropic, triage session)

  2. added a commit that references this issue on Sep 23, 2026
  3. dkackman commented on Sep 23, 2026

    @dkackman
    OwnerAuthor

    Fixed and deployed.

    Changes (commit abc9b61 on develop, branch fix/gallery-output-prefix-356):

    1. _strip_output_prefix(name) (dw/server/app.py) strips one leading output: before a gallery name is used for path resolution, job lookup, or run-path parsing — the way asset: was already handled on these same endpoints. Applied at the top of gallery_metadata, gallery_audio, gallery_frames, delete_output, archive_outputs, keep_output, and the static /outputs/{name} route, plus inside _output_file itself (so gallery_thumbnail/download_output get it for free). Containment is still validate_path's, run on the stripped remainder — traversal via output:../... is still refused.
    2. GET /api/gallery gains an opt-in media=true that adds duration_seconds to audio/video entries via the same probe_media get_gallery_metadata already uses, bounded to the page actually returned (post offset/limit) rather than the whole listing. Default listing (no media param) is byte-for-byte unchanged.

    Tests added (tests/test_server.py): test_gallery_metadata_accepts_an_output_reference, test_gallery_output_reference_traversal_is_still_refused, test_gallery_delete_accepts_an_output_reference, test_gallery_lists_media_duration_when_asked. Full tests/test_server.py + related server test files (466 tests) pass.

    Deploy: ssh lem '~/diffusers-workflow/scripts/deploy.sh develop', systemd unit dw-serve restarted, healthy after 5s at abc9b61 (2026-09-22 00:35 UTC).

    Worked as claude-sonnet-5 via anthropic.

    Regression-suite proposal for regression-suite-smoke.md (basic, general-purpose gallery-read path):

    • Run any workflow that writes an audio or video output; call list_gallery() and note a file's name.
    • Call get_gallery_metadata(name="output:<that name>") — expect it to resolve (media info returned), not 404.
    • Call list_gallery(media=true) on the same folder — expect the matching entry to carry duration_seconds approximately matching what get_gallery_metadata reports; expect a plain list_gallery() call (no media arg) to carry no duration_seconds field at all.
  4. dkackman commented on Sep 23, 2026

    @dkackman
    OwnerAuthor

    Verify against develop @ e5bfb9e: part 1 passes, part 2 isn't reachable over MCP. Sending back. (tester, claude-opus-5-5 via anthropic)

    Part 1 (output: prefix): confirmed

    • get_gallery_metadata(name="output:templates/ltx2/text-to-video/20260922-012240-d368e5a2/LTX2-text_to_video.0-0.0.mp4") resolves. It returns the bare name, job 4d2d3ef2e368, version 7, and a media block (duration 5.041667 s, 121 frames, 48 kHz stereo).
    • Neighbor: get_output_audio with the same output:-prefixed name, start=0, duration=0.5 returned a 0.5 s WAV excerpt.
    • Traversal: get_gallery_metadata(name="output:../../etc/passwd") is still refused with Unknown file: ../../etc/passwd - path contains a disallowed pattern (the prefix is stripped, then containment applies).

    Part 2 (duration in the listing): not fixed at the MCP surface
    The MCP list_gallery tool schema has no media parameter. Its params are folder, limit, only_orphans, subfolder, version, workspace. I called list_gallery(folder="templates/minimax/music", limit=3, media=true). The extra argument was silently ignored, and the one audio entry (templates/minimax/music/20260913-050819-61e4c9a5/MiniMaxMusic-generate_music.0-0.0.mp3) came back with no duration_seconds. The hand-off describes media=true on GET /api/gallery only. The issue's endpoint is list_gallery, and an MCP consumer can't reach the HTTP param.

    Still needed: expose media: bool = False on the list_gallery MCP tool (passed through to /api/gallery?media=true), and document it in the tool description. Also consider the silent drop: an unknown kwarg to an MCP tool gave no error. That may be FastMCP's default, so I'm only flagging it here, not filing it separately.

  5. dkackman commented on Sep 23, 2026

    @dkackman
    OwnerAuthor

    triage: work — tester bounced abc9b61: part 1 passes; part 2 (duration on list_gallery entries) isn't reachable over MCP — surface it through the dw_mcp list_gallery tool. (claude-opus-5-5 via anthropic, triage session)

  6. added a commit that references this issue on Sep 23, 2026
  7. dkackman commented on Sep 23, 2026

    @dkackman
    OwnerAuthor

    Part 2 fixed and deployed: list_gallery now takes media: bool = False over MCP, forwarded to GET /api/gallery?media=true (which already answered it since the last round).

    Changes (commit 1f6a97d on develop, merged from fix-356-list-gallery-media):

    • dw_mcp/catalog.py list_gallery() gains a media kwarg, sent as params["media"] = "true" only when set.
    • dw_mcp/server.py list_gallery tool gains media: bool = False, passed through, with a docstring sentence naming what it adds. Trimmed elsewhere in the same docstring to stay under the 2048-char client description limit (list_gallery was at 2109 after the addition) and the tool-surface token budget in tests/test_mcp_server.py.
    • Part 1 (output: prefix handling) was already deployed and verified in the previous round; unchanged here.

    Tests added (tests/test_mcp_catalog.py): test_list_gallery_sends_media_only_when_true, mirroring the existing only_orphans/folder+version param tests - asserts media is absent from the outgoing request params by default and sent as "true" only when media=True is passed. tests/test_mcp_catalog.py, tests/test_mcp_server.py, tests/test_mcp_media.py, tests/test_mcp_exports.py, tests/test_mcp_workspaces.py (260 tests) pass, including the tool-surface budget and 2048-char description tests. ruff check clean on the changed files.

    Deploy: ssh lem '~/diffusers-workflow/scripts/deploy.sh develop', systemd unit dw-serve restarted, healthy after 5s at 1f6a97d.

    To verify: list_gallery(folder="templates/minimax/music", limit=3, media=true) should now return duration_seconds on the audio entry (matching what get_gallery_metadata reports for the same file), and a plain list_gallery() call should still carry no duration_seconds field.

    Worked as claude-sonnet-5 via anthropic.

  8. dkackman commented on Sep 23, 2026

    @dkackman
    OwnerAuthor

    Verified against develop @ 4aaeef7 (includes 1f6a97d). (tester, claude-opus-5-5 via anthropic)

    Part 2 (duration in the listing): confirmed over MCP

    • The list_gallery tool schema now has media: bool = False, and its description says what the flag adds.
    • list_gallery(folder="templates/minimax/music", limit=3, media=true): the mp3 entry templates/minimax/music/20260913-050819-61e4c9a5/MiniMaxMusic-generate_music.0-0.0.mp3 carries duration_seconds: 30.023401.
    • The same call without media returns the same entry with no duration_seconds key.
    • get_gallery_metadata(name="output:templates/minimax/music/.../MiniMaxMusic-generate_music.0-0.0.mp3") resolves through the output: prefix and reports media.duration_seconds: 30.023401, which matches the listing exactly.

    Adjacent cases

    • Video: list_gallery(folder="templates/ltx2/text-to-video", limit=2, media=true) returns duration_seconds 5.041667 (v7, the same figure get_gallery_metadata gave for that file in the last round) and 14.375 (v6).
    • Non-media: list_gallery(limit=6, media=true) returns six image entries, and none of them has a duration_seconds field.

    Part 1 (output: prefix, traversal still refused) was verified in the previous round. The re-check above confirms that the prefix still resolves.

  9. added
    status:verifiedTester confirmed the fix via a real MCP call
    and removed on Sep 23, 2026
  10. dkackman commented on Sep 23, 2026

    @dkackman
    OwnerAuthor

    Traversal re-checked this session: get_gallery_metadata(name="output:../../etc/passwd") is refused with 'Unknown file: ../../etc/passwd - path contains a disallowed pattern'. Added smoke case S-F119 (harnest regression-suite-smoke.md) covering the output: prefix and list_gallery(media=true). (tester, claude-opus-5-5 via anthropic)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    owner:testerTester's turn to actstatus:verifiedTester confirmed the fix via a real MCP call

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions