langflow-ai/langflow · error · HTTPException

Invalid embedding configuration

Error message

Invalid embedding configuration

What it means

A 400 raised after metadata is loaded: the embedding config payload (model_selection, or the legacy embedding_model/embedding_provider fields) lacks a name or a provider. The KB exists and has metadata, but the embedding configuration recorded on it is incomplete, so ingestion cannot proceed because embeddings cannot be resolved.

Solutions

  1. Reconfigure the KB's embedding settings via the UI/API so both model name and provider are persisted.
  2. Recreate the KB and re-ingest its files.
  3. Fix the metadata file on disk directly: ensure model_selection = {"name": ..., "provider": ...} is complete.
  4. If this repros for newly created KBs, check for a version mismatch between frontend creation flow and backend expectations and update Langflow.

Example fix

# repair on-disk metadata
import json, pathlib
p = pathlib.Path(kb_dir) / "metadata"  # location of KB metadata
meta = json.loads(p.read_text())
meta["model_selection"] = {"name": "openai/text-embedding-3-small", "provider": "openai"}
p.write_text(json.dumps(meta))
Defensive patterns

Strategy: validation

Validate before calling

def has_complete_embedding_config(metadata: dict) -> bool:
    sel = metadata.get("model_selection") or {
        "name": metadata.get("embedding_model"),
        "provider": metadata.get("embedding_provider"),
    }
    return bool(sel.get("name") and sel.get("provider"))

Type guard

from typing import TypedDict

class ModelSelection(TypedDict, total=False):
    name: str
    provider: str

def is_valid_model_selection(ms: dict) -> bool:
    return isinstance(ms, dict) and bool(ms.get("name")) and bool(ms.get("provider"))

Try / catch

try:
    await ingest(files)
except HTTPStatusError as e:
    if e.response.status_code == 400:
        detail = e.response.json()["detail"]
        if detail == "Invalid embedding configuration":
            await reconfigure_kb_embedding(kb, model, provider)

Prevention

When it happens

Trigger: POST /api/v1/knowledge_bases/{kb_name}/upload where the KB metadata exists but model_selection.name or model_selection.provider is empty/missing AND the legacy embedding_model/embedding_provider fallbacks are also empty. Typically hand-edited metadata or a partially written KB config.

Common situations: Manually edited or migrated KB metadata files, a KB created against an older version whose creation flow did not persist provider, or a metadata write interrupted midway.

Related errors


AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14). Data as JSON: /api/errors/697d57d9d3e66a51. Report an issue: GitHub.

Appendix: source

Thrown at src/backend/base/langflow/api/v1/knowledge_bases.py:1097

        # Read embedding metadata (Pass fast=False to ensure legacy KBs are migrated/detected)
        metadata = KBAnalysisHelper.get_metadata(kb_path, fast=False)
        if not metadata:
            raise HTTPException(
                status_code=400,
                detail="Knowledge base missing embedding configuration. Please create a new KB or reconfigure it.",
            )

        # ``model_selection`` is the canonical embedding-config payload.
        # Synthesize it from the legacy flat metadata fields when older
        # KBs only carry those (``record_to_metadata_dict`` writes both
        # forms for new KBs, so this branch is mainly for disk-only
        # ones that haven't been backfilled yet).
        model_selection = metadata.get("model_selection") or {
            "name": metadata.get("embedding_model"),
            "provider": metadata.get("embedding_provider"),
        }
        if not model_selection.get("name") or not model_selection.get("provider"):
            raise HTTPException(status_code=400, detail="Invalid embedding configuration")

        # Use ``KnowledgeBaseRecord.id`` (when present) as the Job's
        # ``asset_id`` so the read path can hit the indexed
        # ``Job.asset_id`` column instead of doing a JSON-extract on
        # ``Job.job_metadata.kb_name``. Falls back to legacy
        # ``metadata['id']`` for KBs that exist on disk only.
        asset_id = await _resolve_kb_asset_id(
            kb_name=kb_name,
            current_user=current_user,
            metadata=metadata,
        )

        # Get services and create job before async/sync split
        job_service = get_job_service()
        job_id = uuid.uuid4()

        # Create job record in database for both async and sync paths
        await job_service.create_job(

View on GitHub (pinned to 976ec789d2)