Skip to content

fix: absolutize symlink target so relative --input-dataset does not produce a dangling videos link - #3

Open
memosr wants to merge 1 commit into
AxisAIOrg:mainfrom
memosr:fix/symlink-abspath
Open

fix: absolutize symlink target so relative --input-dataset does not produce a dangling videos link#3
memosr wants to merge 1 commit into
AxisAIOrg:mainfrom
memosr:fix/symlink-abspath

Conversation

@memosr

@memosr memosr commented Aug 8, 2026

Copy link
Copy Markdown

Summary

copy_tree_or_symlink writes the source path into the symlink verbatim, so a
relative --input-dataset produces a dangling link and the converted dataset
ends up with no readable videos.

axis_train/data/lerobot_v3.py:157:

dst.symlink_to(src, target_is_directory=True)

A symlink target is resolved relative to the directory containing the link, not
relative to the process working directory. LeRobotV3Dataset.from_path does not
resolve the path (lerobot_v3.py:20-21), so src.videos_dir stays exactly as
the caller typed it.

This is the default code path: convert_action_9d_to_8d.py:26 sets
symlink_videos=not args.copy_videos.

Why this is easy to miss

Nothing raises. convert_dataset completes and
data/convert_action_9d_to_8d.py:33 returns 0. The input_dataset and
output_dataset fields written to the report are .resolve()d
(action_conversion.py:194-195), so the report looks correct while the symlink
points nowhere.

Fix

One line, plus the os import:

dst.symlink_to(os.path.abspath(src), target_is_directory=True)

os.path.abspath rather than Path.resolve() on purpose: resolve() would
also follow symlinks inside the source path, which is a separate behaviour
change.

Verification

Ran copy_tree_or_symlink directly against a temporary dataset tree, before and
after:

Case Link target Resolves videos/ readable
Relative --input-dataset, before raw_ds/videos no FileNotFoundError
Relative --input-dataset, after /…/raw_ds/videos yes ['cam.mp4']
Absolute --input-dataset, before /…/raw_ds/videos yes ['cam.mp4']
Absolute --input-dataset, after /…/raw_ds/videos yes ['cam.mp4']

The absolute case is unchanged, which is also the check that resolve() is not
in play: the target keeps its original /var/… form instead of being rewritten
to /private/var/….

The symlink=False branch is untouched.

Scope

One file. No overlap with #1 (CI, tests, pyproject.toml) or #2 (README.md,
training/run_libero_eval.sh).

Caveat

convert_action_9d_to_8d.py is not referenced in the README or under docs/,
so I cannot claim a documented workflow is broken. The claim is narrower: anyone
invoking it with a relative path from the repo root gets a dataset whose videos
are unreachable, with no error.

A symlink target resolves relative to the link's own directory, not the
process cwd. LeRobotV3Dataset.from_path does not resolve its input, so a
relative --input-dataset produced a dangling videos symlink.

The failure is silent: convert_dataset returns 0 and the report records
resolved paths while the link points nowhere.

Uses os.path.abspath rather than Path.resolve to avoid following symlinks
inside the source path. No-op for absolute inputs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant