{"record":{"id":"1de9e5eae54bd254","repo":"jamiepine/voicebox","slug":"destination-cannot-be-inside-the-current-cache-dir","errorCode":null,"errorMessage":"Destination cannot be inside the current cache directory","messagePattern":"Destination cannot be inside the current cache directory","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"backend/routes/models.py","lineNumber":136,"sourceCode":"    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\n\n    async def migrate_background():\n        moved = 0","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/routes/models.py#L118-L154","documentation":"400 from POST /models/migrate. Using Path.is_relative_to, the route rejects a destination whose resolved path is nested inside the resolved source. Moving the cache into itself would create recursive copies or move the directory into a child that gets moved along with it, corrupting the cache layout. Checked after the same-directory guard.","triggerScenarios":"POST /models/migrate with destination like '/current/cache/subdir' or '/current/cache/.backup' — anything where destination.resolve() lives under source.resolve(). Also triggers when destination is a symlink whose target is inside the cache.","commonSituations":"User tries to 'make a backup folder inside the cache'; UI suggests a default destination nested under the cache root; migration retry pointing at a partially-created subdir from a prior failed run; symlink inside cache pointing deeper inside.","solutions":["Pick a destination that is a sibling or on a different volume, not a child of the cache.","GET /models/cache-dir and confirm your destination is not under that path (after resolving symlinks).","If you need a backup, copy the cache elsewhere first, then point migrate at the external copy.","Clean up any stray subdirectories created by a prior failed migration attempt before retrying."],"exampleFix":"# before — destination inside cache\ncurl -X POST http://localhost:8000/models/migrate -d '{\"destination\":\"/Users/me/.cache/huggingface/archive\"}'\n# after\ncurl -X POST http://localhost:8000/models/migrate -d '{\"destination\":\"/Volumes/External/hf-cache\"}'","handlingStrategy":"validation","validationCode":"import { resolve, relative, isAbsolute } from 'path';\nasync function migrateTo(dest: string) {\n  const { path: cacheDir } = await (await fetch('/models/cache-dir')).json();\n  const rel = relative(resolve(cacheDir), resolve(dest));\n  if (isAbsolute(rel) || (!rel.startsWith('..') && rel !== '')) {\n    throw new Error('Destination is inside the 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 && /inside the current cache/i.test(e.response.detail)) {\n    // suggest a sibling or external volume\n  } else throw e;\n}","preventionTips":["Pick a destination outside the cache tree (sibling or other volume).","GET /models/cache-dir and reject nested destinations in the UI before submit.","Resolve symlinks in the destination before the comparison."],"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"}