unslothai/unsloth · error · HTTPException

{model!r} has cached pickle weights that cannot be security-

Error message

{model!r} has cached pickle weights that cannot be security-scanned offline and no safetensors alternative, so it cannot be used as the embedding model. Re-download it with safetensors weights while online.

What it means

HTTP 403 (hard, non-forceable security refusal) from the embedding-model endpoint when local_only_load is true and the requested model has cached pickle-format weights (.bin/.pt) with no safetensors alternative. Pickle weights cannot be security-scanned offline, so the endpoint refuses to use them as the RAG embedding model; unlike the generic verification 409, no force flag bypasses this — you must obtain safetensors weights.

Source

Thrown at studio/backend/routes/settings.py:1625

            model,
            hf_token = scan_token,
            load_subdirs = load_subdirs,
            local_only_load = local_only_load,
        ).blocked:
            # 403, not 409: the client routes every 409 into the forceable "save anyway"
            # flow, but this block is a hard, non-forceable security refusal.
            if local_only_load:
                detail = (
                    f"{model!r} has cached pickle weights that cannot be security-scanned "
                    "offline and no safetensors alternative, so it cannot be used as the "
                    "embedding model. Re-download it with safetensors weights while online."
                )
            else:
                detail = (
                    f"{model!r} is flagged as unsafe by Hugging Face's security scan and "
                    "cannot be used as the embedding model."
                )
            raise HTTPException(status_code = 403, detail = detail)
    if model != default_embedding_model() and not payload.force and not is_local_gguf:
        from core.rag import config as rag_config

        # A GGUF-named repo on the llama-server backend is loaded from its .gguf
        # files, which rarely carry sentence-transformers metadata; verify the
        # GGUF is available (below) rather than the ST embedding-metadata gate,
        # which would wrongly 409 a valid online GGUF embedder.
        gguf_named = _llama_backend_active() and rag_config._names_gguf(model)
        if not gguf_named and not is_embedding_model(model, hf_token = hf_token):
            # Offline, is_embedding_model can only confirm the ST layout (modules.json); a
            # transformers-native embedder (e.g. gte-modernbert) is unverifiable without Hub
            # metadata. If already cached and loadable, accept it rather than raising a 409 that
            # online would not (ST can load any cached encoder). Uncached -> 409.
            from utils.utils import hf_cache_snapshot_is_loadable

            # Require a genuinely loadable cache (config + weights), not just a resolved refs/main,
            # so a metadata-only partial cache still gets the forceable 409.
            offline_cached = local_only_load and hf_cache_snapshot_is_loadable(model)

View on GitHub (pinned to 203007d190)

Solutions

  1. Bring the server online once and re-download the model so safetensors weights are fetched alongside (or instead of) the pickle files.
  2. Or delete the pickle-only cache entry and download the repo with safetensors on a connected machine, then copy the cache over.
  3. Do not attempt to bypass with force:true — this branch intentionally ignores it.
  4. Prefer repos that publish safetensors (most current sentence-transformers models do).

Example fix

# before (offline, pickle-only cache)
PUT /settings/embedding-model {"model": "old-model-with-.bin-only"}  # 403

# after (one-time online refresh)
huggingface-cli download old-model --include "*.safetensors" "*.json"
# restart offline mode; the same PUT now passes the scan gate
Defensive patterns

Strategy: validation

Validate before calling

const files = await listLocalCacheFiles(model);
const hasSafetensors = files.some(f => f.endsWith('.safetensors'));
if (!hasSafetensors) throw new Error('Model lacks safetensors; refresh online before offline use');
await api.put('/settings/embedding-model', { model });

Type guard

function isOfflineSafeEmbedder(files: string[]): boolean {
  return files.some(f => f.endsWith('.safetensors'));
}

Try / catch

try { await api.put('/settings/embedding-model', { model }); }
catch (e) {
  if (e.status === 403 && /pickle/i.test(e.detail)) { queueOnlineRedownload(model); return; } // not forceable
  throw e;
}

Prevention

When it happens

Trigger: PUT the embedding-model setting to a repo whose local cache only contains pickle weight files, while the server runs in local/offline-only load mode (no Hub access).

Common situations: Pre-2023 sentence-transformers snapshots downloaded before safetensors became standard; air-gapped machines; a cache populated by an older tool that skipped safetensors; partial downloads that kept the .bin files.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/63ea11896cd277e1. Report an issue: GitHub.