Summary
The new UUID-keyed mount contract introduced in #11434 (and refined in #11520) carries no per-mount source-subpath. There is currently no way to express "mount the <X> subdirectory of vfolder <Y> at container path <Z>" through any session-creation API surface, even though the lower-level mount machinery still supports it.
#11520 acknowledges this deferral explicitly:
Subpath syntax (name/subdir) remains rejected — full subpath support requires a separate, larger change that plumbs vfsubpath through MountInfoEntry / VFolderMountRequest.
This issue is that follow-up.
Where the capability disappeared
src/ai/backend/manager/api/rest/session/handler.py:327-333 — rejects every mounts entry containing /.
src/ai/backend/common/types.py:655-684 — MountInfoEntry carries only vfolder_id, mount_destination, mount_perm. No source-subpath slot.
src/ai/backend/manager/registry.py:_mount_entries_from_creation_config — projects creation_config["mount_ids"] / mount_id_map / mount_options into MountInfoEntry tuples; no slot to populate even if the wire format gained one.
src/ai/backend/manager/repositories/scheduler/db_source/db_source.py:1572-1580 — reconstitutes VFolderMountRequest(ref=UUID(...)) from MountInfoEntry. The string-form ref (which prepare_vfolder_mounts knows how to split into name + subpath) is no longer reachable from this path.
Where the capability still exists, just unreachable
src/ai/backend/manager/models/vfolder/row.py:925-940 (prepare_vfolder_mounts) still handles name/subpath strings:
for req in mount_requests:
key = req.ref
if isinstance(key, uuid.UUID):
requested_vfolder_ids.add(key)
continue
name, _, subpath = key.partition("/")
requested_vfolder_subpaths[key] = os.path.normpath(subpath)
When ref is a UUID, the subpath is forced to "." (mount root). The infrastructure to mount a subpath is intact — only the API-level addressability was removed.
Impact
Any caller that previously mounted a subdirectory of an existing vfolder at a specific container destination has to either:
- Replace each subdir with its own dedicated vfolder (costly migration; changes the user-visible vfolder layout).
- Mount the whole parent vfolder and live with deeper container-visible paths than before.
Neither preserves backwards compatibility for callers that built on the previous behavior. The pattern was common — typical layouts include <parent>/.shared/, <parent>/.scratch/<task_id>/, <parent>/inputs/... mounted at distinct container paths. With #11434's contract there is no way to express these without rearchitecting the parent vfolder into N separate vfolders per workflow.
Proposed fix
Re-introduce a per-mount source subpath on the contract:
- Add
source_subpath: str | None = None to MountInfoEntry (or equivalent name; carries the subdir relative to the vfolder root).
- Plumb it through
_mount_entries_from_creation_config. Wire it via either:
- A new
mount_id_subpaths: Mapping[UUID-str, str] field on creation_config, or
- A richer dict shape inside
mount_id_map (e.g. {uuid: {"dst": "/x", "subpath": ".shared"}}).
The first form is simpler and avoids reshaping an existing field.
- Pass
source_subpath through to VFolderMountRequest (which already accepts str ref with a / and parses it). One option: when source_subpath is set, build the ref as f"{name}/{source_subpath}" against the resolved vfolder name. Another: extend VFolderMountRequest directly with a subpath: str | None field. The latter is cleaner since it removes the string-format ambiguity and makes the data path explicit.
- Soften the rejection at
_resolve_legacy_name_mounts:327-333 so legacy <name>/<subpath> entries on the deprecated mounts field are accepted again, resolved to (uuid, subpath), and forwarded through the new field. (This is optional but lets pre-existing v1 callers continue working without rewrite.)
Acceptance criteria
MountInfoEntry (or its equivalent) has a source_subpath field; None means "mount the vfolder root" (current behavior).
POST /session/_/create with a UUID-keyed mount_ids plus a per-mount source_subpath results in a session where the container destination is bind-mounted to <vfolder>/<source_subpath> rather than the vfolder root.
- The legacy
mounts: ["<name>/<subpath>"] form on the same endpoint stops raising InvalidAPIParameters(400) and produces an equivalent mount.
prepare_vfolder_mounts is exercised end-to-end with a non-"." subpath via the modern UUID-keyed surface (existing tests cover the legacy string surface).
- No change to
MountPermission, mount destination defaulting, or storage-proxy vfsubpath semantics.
Related
Summary
The new UUID-keyed mount contract introduced in #11434 (and refined in #11520) carries no per-mount source-subpath. There is currently no way to express "mount the
<X>subdirectory of vfolder<Y>at container path<Z>" through any session-creation API surface, even though the lower-level mount machinery still supports it.#11520 acknowledges this deferral explicitly:
This issue is that follow-up.
Where the capability disappeared
src/ai/backend/manager/api/rest/session/handler.py:327-333— rejects everymountsentry containing/.src/ai/backend/common/types.py:655-684—MountInfoEntrycarries onlyvfolder_id,mount_destination,mount_perm. No source-subpath slot.src/ai/backend/manager/registry.py:_mount_entries_from_creation_config— projectscreation_config["mount_ids"]/mount_id_map/mount_optionsintoMountInfoEntrytuples; no slot to populate even if the wire format gained one.src/ai/backend/manager/repositories/scheduler/db_source/db_source.py:1572-1580— reconstitutesVFolderMountRequest(ref=UUID(...))fromMountInfoEntry. The string-formref(whichprepare_vfolder_mountsknows how to split into name + subpath) is no longer reachable from this path.Where the capability still exists, just unreachable
src/ai/backend/manager/models/vfolder/row.py:925-940(prepare_vfolder_mounts) still handlesname/subpathstrings:When
refis aUUID, the subpath is forced to"."(mount root). The infrastructure to mount a subpath is intact — only the API-level addressability was removed.Impact
Any caller that previously mounted a subdirectory of an existing vfolder at a specific container destination has to either:
Neither preserves backwards compatibility for callers that built on the previous behavior. The pattern was common — typical layouts include
<parent>/.shared/,<parent>/.scratch/<task_id>/,<parent>/inputs/...mounted at distinct container paths. With #11434's contract there is no way to express these without rearchitecting the parent vfolder into N separate vfolders per workflow.Proposed fix
Re-introduce a per-mount source subpath on the contract:
source_subpath: str | None = NonetoMountInfoEntry(or equivalent name; carries the subdir relative to the vfolder root)._mount_entries_from_creation_config. Wire it via either:mount_id_subpaths: Mapping[UUID-str, str]field oncreation_config, ormount_id_map(e.g.{uuid: {"dst": "/x", "subpath": ".shared"}}).The first form is simpler and avoids reshaping an existing field.
source_subpaththrough toVFolderMountRequest(which already acceptsstrrefwith a/and parses it). One option: whensource_subpathis set, build therefasf"{name}/{source_subpath}"against the resolved vfolder name. Another: extendVFolderMountRequestdirectly with asubpath: str | Nonefield. The latter is cleaner since it removes the string-format ambiguity and makes the data path explicit._resolve_legacy_name_mounts:327-333so legacy<name>/<subpath>entries on the deprecatedmountsfield are accepted again, resolved to(uuid, subpath), and forwarded through the new field. (This is optional but lets pre-existing v1 callers continue working without rewrite.)Acceptance criteria
MountInfoEntry(or its equivalent) has asource_subpathfield;Nonemeans "mount the vfolder root" (current behavior).POST /session/_/createwith a UUID-keyedmount_idsplus a per-mountsource_subpathresults in a session where the container destination is bind-mounted to<vfolder>/<source_subpath>rather than the vfolder root.mounts: ["<name>/<subpath>"]form on the same endpoint stops raisingInvalidAPIParameters(400)and produces an equivalent mount.prepare_vfolder_mountsis exercised end-to-end with a non-"."subpath via the modern UUID-keyed surface (existing tests cover the legacy string surface).MountPermission, mount destination defaulting, or storage-proxyvfsubpathsemantics.Related
mountsfield rejects UUID-shaped strings (BA-5916 follow-up) #11520 — closed follow-up that explicitly deferred this work.