jamiepine/voicebox · warning · HTTPException
Source and destination are the same directory
Error message
Source and destination are the same directory
What it means
400 from POST /models/migrate. After resolving both paths, the route compares source.resolve() == destination.resolve(); if they refer to the same directory (after symlink resolution) it refuses with HTTPException(400, 'Source and destination are the same directory'). This is a guard against a no-op/in-place move that could destroy the cache.
Source
Thrown at backend/routes/models.py:133
"""Get the path to the HuggingFace model cache directory."""
from huggingface_hub import constants as hf_constants
return {"path": str(Path(hf_constants.HF_HUB_CACHE))}
@router.post("/models/migrate")
async def migrate_models(request: models.ModelMigrateRequest):
"""Move all downloaded models to a new directory with byte-level progress via SSE."""
from huggingface_hub import constants as hf_constants
source = Path(hf_constants.HF_HUB_CACHE)
destination = Path(request.destination)
if not source.exists():
raise HTTPException(status_code=404, detail="Current model cache directory not found")
if source.resolve() == destination.resolve():
raise HTTPException(status_code=400, detail="Source and destination are the same directory")
if destination.resolve().is_relative_to(source.resolve()):
raise HTTPException(status_code=400, detail="Destination cannot be inside the current cache directory")
progress_manager = get_progress_manager()
model_dirs = [d for d in source.iterdir() if d.name.startswith("models--") and d.is_dir()]
if not model_dirs:
progress_manager.update_progress("migration", 1, 1, status="complete")
progress_manager.mark_complete("migration")
return {"moved": 0, "errors": [], "source": str(source), "destination": str(destination)}
destination.mkdir(parents=True, exist_ok=True)
same_fs = False
try:
same_fs = source.stat().st_dev == destination.stat().st_dev
except OSError:
passView on GitHub (pinned to 51f49dea19)
Solutions
- Choose a destination on a different mount/volume (the whole point of migrate is usually to move across filesystems).
- GET /models/cache-dir to see the current source, then pick a destination whose realpath differs.
- If you intended to reorganize in place, that's not what migrate does — use filesystem tools directly.
- Resolve symlinks in the destination before sending (readlink -f) to confirm it differs from source.
Example fix
# before
curl -X POST http://localhost:8000/models/migrate -d '{"destination":"/Users/me/.cache/huggingface"}'
# (same as source)
# after
curl -X POST http://localhost:8000/models/migrate -d '{"destination":"/Volumes/External/models"}' Defensive patterns
Strategy: validation
Validate before calling
import { resolve } from 'path';
async function migrateTo(dest: string) {
const { path: cacheDir } = await (await fetch('/models/cache-dir')).json();
if (resolve(dest) === resolve(cacheDir)) {
throw new Error('Destination equals the current cache directory');
}
return await fetch('/models/migrate', {method:'POST', body: JSON.stringify({destination: dest})});
} Try / catch
try {
await fetch('/models/migrate', {method:'POST', body: JSON.stringify({destination})});
} catch (e) {
if (e.response?.status === 400 && /same directory/i.test(e.response.detail)) {
// prompt user for a different destination
} else throw e;
} Prevention
- Pre-fill the destination with a path on a different volume, not the current cache path.
- Resolve symlinks before comparing source vs destination.
- GET /models/cache-dir to display the source prominently so users don't re-enter it.
When it happens
Trigger: POST /models/migrate body {"destination": "/exact/same/path/as/cache"}; destination is a symlink that resolves to the cache dir; destination is '.' or the literal HF_HUB_CACHE path; relative destination that resolves under the current working directory equal to source.
Common situations: User pasted the current cache path into the destination field by mistake; UI pre-filled destination with the source; symlink aliasing the cache; running migrate twice with the second call pointing at where the first already moved things.
Related errors
- Destination cannot be inside the current cache directory
- Unknown model: {model_name}
- Current model cache directory not found
- Unknown model: {request.model_name}
- Model ${model_size} is not downloaded yet. Use /generate to
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/adbf2c1b4c995fab.
Report an issue: GitHub.