|

fastembed cache en Windows: por qué se rompe y cómo anclarlo

En pocas palabras: fastembed guarda el modelo ONNX en la carpeta Temp de Windows (AppData\Local\Temp\fastembed_cache), que el sistema limpia solo y termina lanzando el error NO_SUCHFILE. La solución es anclar el cache a una carpeta estable con el parámetro cache_dir o la variable FASTEMBED_CACHE_PATH.

Si fastembed venía funcionando y de un día para el otro te tira NO_SUCHFILE sobre un .onnx que ayer estaba, el problema casi nunca es el código: es dónde guarda el modelo. Por defecto lo tira en la carpeta Temp de Windows, y Windows limpia esa carpeta solo. La solución es anclar el cache del modelo a una carpeta estable con cache_dir o la variable FASTEMBED_CACHE_PATH.

fastembed es una librería de Python de Qdrant que genera embeddings (vectores numéricos que representan texto) usando modelos ONNX livianos, sin arrastrar PyTorch ni dependencias pesadas. Cuando la inicializás sin decirle dónde guardar el modelo, usa tempfile.gettempdir(), o sea el directorio temporal del sistema operativo. En Windows eso es C:\Users\<usuario>\AppData\Local\Temp\fastembed_cache\.

En 30 segundos

  • La causa: sin cache_dir, fastembed guarda el modelo en la carpeta Temp de Windows (AppData\Local\Temp\fastembed_cache).
  • El síntoma: el WARNING “Local file sizes do not match the metadata” seguido de NO_SUCHFILE del runtime ONNX.
  • Por qué pasa: Storage Sense y la limpieza de disco de Windows purgan Temp, y lo hacen de forma no atómica, así que borran algunos archivos del snapshot y dejan otros.
  • El fix: pasar cache_dir a una carpeta del proyecto o setear FASTEMBED_CACHE_PATH en el entorno.
  • En workers efímeros: volumen persistente montado o modelo cargado en memoria como singleton, nunca el disco temporal.

¿Por qué fastembed guarda los modelos en la carpeta Temp de Windows?

Porque cuando no le pasás el argumento cache_dir, fastembed cae en su fallback: el directorio temporal del sistema operativo que devuelve tempfile.gettempdir(). En Windows esa ruta es C:\Users\<usuario>\AppData\Local\Temp\fastembed_cache\. Es un comportamiento heredado de las librerías de ML, que asumen que el temp del sistema es un lugar razonable para dejar pesos descargados.

El caso que documentó Yogesh Chavan en su nota de dev.to es exacto: su inicialización no apuntaba a ningún lado. Algo así.

# src/pacos/knowledge/index/embeddings.py
from fastembed import TextEmbedding

# sin cache_dir: fastembed usa el temp del SO
model = TextEmbedding("BAAI/bge-small-en-v1.5")

Fijate que no hay una sola línea que mencione una ruta de cache. Ese silencio es el que te manda el modelo (unos 90 MB de .onnx) a una carpeta que no controlás. Y ahí, tarde o temprano, alguien más mete mano. Relacionado: en vulnerabilidades de pipelines CI/CD.

¿Qué pasa cuando Windows limpia la carpeta Temp automáticamente?

Que te borra parte del modelo sin avisar. Windows corre Storage Sense y la limpieza de disco en segundo plano (a veces también en algún reinicio, según la configuración), y esas rutinas apuntan justo a AppData\Local\Temp. El detalle clave: no borran de forma atómica. Pueden eliminar un subconjunto de archivos dentro de una carpeta de snapshot y dejar el resto intacto.

Ese es el origen del warning tan raro. El manifiesto del modelo dice que el snapshot tiene N archivos de ciertos tamaños. En disco, uno falta. De ahí sale el Local file sizes do not match the metadata, y después el error duro cuando ONNX intenta cargar el model_optimized.onnx que ya no está.

Ponele que le pedís a tu worker que embeba una query. Ayer anduvo. Hoy, con el mismo código y la misma máquina, explota. En el medio no tocaste nada: pasó una limpieza de disco.

¿Cómo saber si es cache roto y no una descarga corrupta?

Mirá dos señales: el mensaje “Local file sizes do not match the metadata” más un NO_SUCHFILE sobre un archivo puntual. Eso es borrado parcial, no descarga fallida. Una descarga corrupta te da checksum mismatch o un archivo truncado, no un archivo que directamente desapareció mientras sus vecinos siguen ahí. Sobre eso hablamos en cuando evalúes tu orquestador.

La regla práctica es simple. Si ayer funcionaba, hoy no, y no cambiaste ni el código ni el modelo ni la máquina, es limpieza de cache. Una descarga corrupta te falla la primera vez, no de un día para el otro. Para confirmarlo, entrá a la ruta del snapshot y verificá si model_optimized.onnx existe:

# PowerShell
Test-Path "$env:LOCALAPPDATA\Temp\fastembed_cache\models--qdrant--bge-small-en-v1.5-onnx-q\snapshots\*\model_optimized.onnx"

¿Devuelve False pero la carpeta del modelo existe? Ahí tenés la prueba: el snapshot está incompleto.

¿Cómo configurar cache_dir para anclar el modelo a una carpeta estable?

Tenés tres caminos, y todos apuntan a lo mismo: sacar el cache de Temp. El más directo es pasar cache_dir al constructor. También podés setear la variable de entorno FASTEMBED_CACHE_PATH, o el argumento cache_dir si usás el wrapper de LangChain. Este es el patrón mínimo:

from fastembed import TextEmbedding

# opción 1: carpeta dentro del proyecto, fuera de Temp
model = TextEmbedding(
 "BAAI/bge-small-en-v1.5",
 cache_dir="./.fastembed_cache"
)
MétodoCómo se seteaCuándo conviene
cache_dir en el constructorTextEmbedding(model, cache_dir="./ruta")Scripts y apps donde controlás el código
FASTEMBED_CACHE_PATHVariable de entorno del procesoCI/CD y contenedores, cambiás la ruta sin recompilar
cache_dir en LangChainFastEmbedEmbeddings(cache_dir="./ruta")Pipelines RAG armados sobre LangChain
fastembed cache windows diagrama explicativo

Ojo con la precedencia: el parámetro cache_dir del constructor le gana a la variable de entorno. Una carpeta dentro del proyecto (o un volumen que vos administrás) es más segura que AppData porque ninguna rutina del sistema la va a tocar. Por convención, en Linux se suele usar ~/.cache/fastembed y en macOS ~/Library/Caches, pero lo importante es que sea persistente y tuya. En entre Jenkins y GitHub Actions profundizamos sobre esto.

¿Dónde guardar embeddings en producción: cache local o base vectorial?

Son dos cosas distintas y conviene no mezclarlas. El cache_dir es donde fastembed baja el modelo .onnx (chico, ~90 MB, se descarga una vez). El almacenamiento de embeddings es donde guardás los vectores que generás, que pueden ser millones y van a una base pensada para eso.

En producción la arquitectura sana tiene tres niveles:

  • Almacenamiento persistente para el modelo: el cache_dir anclado a disco o volumen, para no rebajar los 90 MB en cada arranque.
  • Base vectorial para los vectores: Qdrant, DuckDB o similar, donde vivís los embeddings ya calculados.
  • Cache en RAM para queries calientes: Redis para no recalcular embeddings de consultas repetidas. La documentación de RedisVL describe este patrón de embeddings cache.

Si estás montando la infraestructura sobre un VPS o un servidor administrado, un volumen persistente para el cache del modelo lo resolvés desde el panel de tu proveedor. En Argentina eso lo tenés en donweb.com con almacenamiento que sobrevive a los reinicios, que es justo lo que Temp no te da.

¿Cuál es el setup recomendado para workers y cron jobs?

En un worker efímero (Lambda, un contenedor que se reinicia, una tarea de Celery) el cache en disco local no sirve: lo perdés en cada restart. Tenés dos opciones que sí aguantan. La primera es cargar el modelo en memoria una sola vez con un patrón singleton, para que las invocaciones sucesivas lo reusen sin volver a leer disco:

_model = None

def get_model():
 global _model
 if _model is None:
 _model = TextEmbedding(
 "BAAI/bge-small-en-v1.5",
 cache_dir="/data/models" # volumen persistente montado
 )
 return _model

La segunda es montar un volumen persistente (Docker o Kubernetes) y apuntar FASTEMBED_CACHE_PATH ahí. Con Docker, un -v models:/data/models y listo. Lo que no querés nunca es dejar que el worker escriba en /tmp o en Temp esperando encontrarlo después, porque en el próximo ciclo puede no estar.

Errores comunes al configurar el cache de fastembed

  • Asumir que el fallback a Temp es estable: no lo es. Si no pasás cache_dir, quedás a merced de Storage Sense. Anclá siempre la ruta, aunque sea en desarrollo.
  • Setear la variable pero no crear la carpeta: FASTEMBED_CACHE_PATH apunta a un directorio que tiene que existir y ser escribible por el proceso. Precreala en el volumen y revisá permisos del usuario del worker.
  • Confundir el cache del modelo con el store de vectores: cache_dir guarda el .onnx, no tus embeddings. Los vectores van a una base vectorial aparte.
  • Usar disco local en un contenedor efímero: si el filesystem se resetea al reiniciar, el modelo se baja de nuevo cada vez (o falla). Volumen persistente o memoria.
  • Leer el NO_SUCHFILE como bug de fastembed: casi siempre es el entorno borrando el cache, no la librería. Verificá el disco antes de abrir una issue.

Preguntas Frecuentes

¿Por qué fastembed pierde el modelo en Windows?

Porque sin cache_dir guarda el modelo en C:\Users\<usuario>\AppData\Local\Temp\fastembed_cache\, y Windows purga esa carpeta con Storage Sense y la limpieza de disco. La purga no es atómica, así que borra algunos archivos del snapshot y deja otros, dejando el modelo incompleto. Te puede servir nuestra cobertura de que ya hemos analizado.

¿Qué significa “Local file sizes do not match the metadata”?

Significa que el manifiesto del modelo espera cierta cantidad de archivos con tamaños definidos, pero en disco falta alguno o no coincide. En el caso de fastembed en Windows, es la firma de un cache borrado parcialmente, no de una descarga corrupta.

¿Cómo configurar FASTEMBED_CACHE_PATH en variables de entorno?

Definí FASTEMBED_CACHE_PATH apuntando a una carpeta persistente y precreada, por ejemplo FASTEMBED_CACHE_PATH=/data/models en Linux o una ruta fija en Windows. El proceso tiene que tener permiso de escritura sobre ese directorio. El parámetro cache_dir del constructor, si lo pasás, tiene prioridad sobre esta variable.

¿Dónde debo guardar el cache de fastembed en producción?

En un volumen persistente montado, nunca en el disco temporal ni en el filesystem efímero de un contenedor. Separá el cache del modelo (el .onnx de ~90 MB) del almacenamiento de vectores, que va a una base vectorial como Qdrant o DuckDB.

¿Sirve el mismo enfoque en Linux con /tmp?

Sí. En Linux el fallback también apunta al temporal del sistema, y muchas distros y contenedores limpian /tmp en el arranque o por systemd-tmpfiles. La regla es idéntica: anclá el cache con cache_dir o FASTEMBED_CACHE_PATH a una carpeta que vos controlés.

Conclusión

El bug no está en fastembed: está en confiar en una carpeta que el sistema operativo considera descartable. Mientras dejes el cache en Temp o en /tmp, cualquier limpieza de disco te puede voltear un worker que venía andando perfecto, y encima el error no apunta a la causa real.

La corrección lleva una línea. Pasá cache_dir a una carpeta estable o seteá FASTEMBED_CACHE_PATH, precreá el directorio con permisos de escritura, y en producción usá un volumen persistente o cargá el modelo en memoria como singleton. Hacelo hoy, antes de que la próxima corrida de Storage Sense te lo recuerde de la peor manera.

Fuentes

Te puede interesar...