{"record":{"id":"adbf2c1b4c995fab","repo":"jamiepine/voicebox","slug":"source-and-destination-are-the-same-directory","errorCode":null,"errorMessage":"Source and destination are the same directory","messagePattern":"Source and destination are the same directory","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"backend/routes/models.py","lineNumber":133,"sourceCode":"    \"\"\"Get the path to the HuggingFace model cache directory.\"\"\"\n    from huggingface_hub import constants as hf_constants\n\n    return {\"path\": str(Path(hf_constants.HF_HUB_CACHE))}\n\n\n@router.post(\"/models/migrate\")\nasync def migrate_models(request: models.ModelMigrateRequest):\n    \"\"\"Move all downloaded models to a new directory with byte-level progress via SSE.\"\"\"\n    from huggingface_hub import constants as hf_constants\n\n    source = Path(hf_constants.HF_HUB_CACHE)\n    destination = Path(request.destination)\n\n    if not source.exists():\n        raise HTTPException(status_code=404, detail=\"Current model cache directory not found\")\n\n    if source.resolve() == destination.resolve():\n        raise HTTPException(status_code=400, detail=\"Source and destination are the same directory\")\n\n    if destination.resolve().is_relative_to(source.resolve()):\n        raise HTTPException(status_code=400, detail=\"Destination cannot be inside the current cache directory\")\n\n    progress_manager = get_progress_manager()\n    model_dirs = [d for d in source.iterdir() if d.name.startswith(\"models--\") and d.is_dir()]\n    if not model_dirs:\n        progress_manager.update_progress(\"migration\", 1, 1, status=\"complete\")\n        progress_manager.mark_complete(\"migration\")\n        return {\"moved\": 0, \"errors\": [], \"source\": str(source), \"destination\": str(destination)}\n\n    destination.mkdir(parents=True, exist_ok=True)\n\n    same_fs = False\n    try:\n        same_fs = source.stat().st_dev == destination.stat().st_dev\n    except OSError:\n        pass","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/routes/models.py#L115-L151","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\ncurl -X POST http://localhost:8000/models/migrate -d '{\"destination\":\"/Users/me/.cache/huggingface\"}'\n# (same as source)\n# after\ncurl -X POST http://localhost:8000/models/migrate -d '{\"destination\":\"/Volumes/External/models\"}'","handlingStrategy":"validation","validationCode":"import { resolve } from 'path';\nasync function migrateTo(dest: string) {\n  const { path: cacheDir } = await (await fetch('/models/cache-dir')).json();\n  if (resolve(dest) === resolve(cacheDir)) {\n    throw new Error('Destination equals the current cache directory');\n  }\n  return await fetch('/models/migrate', {method:'POST', body: JSON.stringify({destination: dest})});\n}","typeGuard":null,"tryCatchPattern":"try {\n  await fetch('/models/migrate', {method:'POST', body: JSON.stringify({destination})});\n} catch (e) {\n  if (e.response?.status === 400 && /same directory/i.test(e.response.detail)) {\n    // prompt user for a different destination\n  } else throw e;\n}","preventionTips":["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."],"tags":["models","migration","validation","filesystem","http-400"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}