unslothai/unsloth · error · ValueError
dataset path must be relative or under a dataset root: {raw!
Error message
dataset path must be relative or under a dataset root: {raw!r} What it means
Raised by resolve_dataset_path() when the input is an absolute path that is not contained under any of the three allowed dataset roots (datasets_root(), dataset_uploads_root(), recipe_datasets_root()). The function only accepts relative paths (optionally prefixed with 'uploads/', 'recipes/', or 'assets/datasets/') or absolute paths already inside a managed root; anything else absolute is rejected to prevent reading arbitrary filesystem locations.
Source
Thrown at studio/backend/utils/paths/storage_roots.py:510
strip_prefixes = ("runs", "tensorboard"),
)
def resolve_dataset_path(path_value: str) -> Path:
raw = str(path_value or "").strip()
if "\x00" in raw:
raise ValueError("dataset path may not contain null bytes")
path = Path(raw).expanduser()
if ".." in path.parts:
raise ValueError(f"dataset path may not contain '..' segments: {raw!r}")
if path.is_absolute():
for root_fn in (datasets_root, dataset_uploads_root, recipe_datasets_root):
try:
_assert_contained(path, root_fn())
return path
except ValueError:
continue
raise ValueError(f"dataset path must be relative or under a dataset root: {raw!r}")
parts = [part for part in Path(path_value).parts if part not in ("", ".")]
if parts[:2] == ["assets", "datasets"]:
parts = parts[2:]
if parts and parts[0] == "uploads":
cleaned = Path(*parts[1:]) if len(parts) > 1 else Path()
return dataset_uploads_root() / cleaned
if parts and parts[0] == "recipes":
cleaned = Path(*parts[1:]) if len(parts) > 1 else Path()
return recipe_datasets_root() / cleaned
cleaned = Path(*parts) if parts else Path()
candidates = [
dataset_uploads_root() / cleaned,
recipe_datasets_root() / cleaned,
datasets_root() / cleaned,
dataset_uploads_root() / cleaned.name,
recipe_datasets_root() / cleaned.name,View on GitHub (pinned to 203007d190)
Solutions
- Send the path relative to the dataset root (or with the 'uploads/'/'recipes/' prefix) instead of absolute.
- If the path should be valid, verify the configured dataset roots actually contain it: check datasets_root()/dataset_uploads_root()/recipe_datasets_root() return values and move/symlink the data or update configuration.
- For data living outside the roots, copy or import it into a managed dataset root first, then reference it relatively.
Example fix
# before
resolve_dataset_path('/srv/old-storage/datasets/cats') # roots moved to /srv/new-storage
# after
resolve_dataset_path('cats') # relative to datasets_root()
# or copy data under the new root first:
# shutil.copytree('/srv/old-storage/datasets/cats', datasets_root() / 'cats') Defensive patterns
Strategy: validation
Validate before calling
from utils.paths.storage_roots import datasets_root, dataset_uploads_root, recipe_datasets_root
def is_under_dataset_root(p) -> bool:
try:
p.relative_to(datasets_root()); return True
except ValueError: pass
for root in (dataset_uploads_root(), recipe_datasets_root()):
try:
p.relative_to(root); return True
except ValueError: pass
return False
if Path(raw).is_absolute() and not is_under_dataset_root(Path(raw).expanduser()):
raise HTTPBadRequest('send dataset paths relative to the dataset root') Type guard
def is_resolvable_dataset_path(raw: str) -> bool:
p = Path(str(raw or '').strip()).expanduser()
if '\x00' in raw or '..' in p.parts:
return False
return not p.is_absolute() or is_under_dataset_root(p) Try / catch
try:
path = resolve_dataset_path(raw)
except ValueError as e:
if 'must be relative or under a dataset root' in str(e):
return bad_request('dataset path outside managed storage')
raise Prevention
- Persist dataset references relative to the dataset root, not absolute — storage roots move between machines.
- After changing storage configuration, run a sweep over stored dataset paths to re-base any absolute ones.
When it happens
Trigger: Calling resolve_dataset_path('/home/user/my-data.csv') where that path is outside all configured dataset roots; also '/var/data/uploads/x' when dataset_uploads_root() points elsewhere (e.g. changed storage configuration), or an absolute path on a different drive/prefix spelling than the root (symlinked mount, trailing differences).
Common situations: Storage root configuration changed (moved datasets directory, different machine) while persisted absolute paths in the DB still point at the old location. Users pasting local absolute paths into a form. Case-sensitivity or symlink mismatches on macOS/Windows making a logically-inside path fail the containment assert.
Related errors
- local cache path must not contain '..' segments
- dataset path may not contain null bytes
- dataset path may not contain '..' segments: {raw!r}
- This execution artifact is outside the Recipe Studio dataset
- Execution artifact path is not a dataset folder.
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/8ac4ed3a483190b5.
Report an issue: GitHub.