infiniflow/ragflow · error · LookupError

TenantModel id={model_id} not found.

Error message

TenantModel id={model_id} not found.

What it means

LookupError raised by TenantModelService.get_model_config_by_id when TenantModelService.get_by_id(model_id) returns exist=False. The function resolves a tenant_model row by its CharField primary key before checking status, model_type bits, and provider ownership; a missing row fails at the first gate. It signals the model id stored on the caller's entity (chat assistant, canvas, dataset embedding setting) does not exist in the tenant_model table.

Source

Thrown at api/db/joint_services/tenant_model_service.py:330

            # SoMark/OCR factories read parser config (somark_*, parse_method, ...)
            # from model_config["extra"]; see tenant_llm_service.LLMBundle OCR path.
            model_config["extra"] = model_extra

        if api_key_payload is not None:
            model_config["api_key_payload"] = api_key_payload

        return model_config
    else:
        raise LookupError(f"Model {model_name} not found for model {model_type_val}")


def get_model_config_by_id(tenant_id: str, model_type: str | enum.Enum, model_id: str):
    """Get model config from tenant_model by its id (CharField PK)."""
    model_type_val = model_type if isinstance(model_type, str) else model_type.value
    model_type_bin = calculate_model_type(model_type_val)
    exist, model_obj = TenantModelService.get_by_id(model_id)
    if not exist:
        raise LookupError(f"TenantModel id={model_id} not found.")
    if model_obj.status == ActiveStatusEnum.INACTIVE.value:
        raise LookupError(f"TenantModel id={model_id} is disabled.")
    if model_obj.status == ActiveStatusEnum.UNSUPPORTED.value:
        raise LookupError(f"TenantModel id={model_id} cannot be used as {model_type_val} model.")
    if not (model_obj.model_type & model_type_bin):
        raise LookupError(f"TenantModel id={model_id} cannot be used as {model_type_val} model.")

    ok, provider_obj = TenantModelProviderService.get_by_id(model_obj.provider_id)
    if not ok:
        raise LookupError(f"Provider id={model_obj.provider_id} not found for model id={model_id}.")

    # Validate that tenant_id owns the provider or is a joined tenant of the provider's owner.
    if tenant_id != provider_obj.tenant_id:
        joined_tenants = TenantService.get_joined_tenants_by_user_id(tenant_id)
        joined_tenant_ids = [t["tenant_id"] for t in joined_tenants]
        if provider_obj.tenant_id not in joined_tenant_ids:
            raise LookupError(f"Tenant {tenant_id} has no access to provider owned by tenant {provider_obj.tenant_id}.")

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Look up the model id in the Model Providers page (or SELECT * FROM tenant_model WHERE id=...) to confirm the row exists in the current database.
  2. If the row is gone, re-select the model on the consuming entity (dataset embedding model, assistant LLM) so a fresh tenant_model id is persisted.
  3. If deleting models, first repoint or delete dependent datasets/assistants to avoid dangling references.
  4. Guard call sites with get_model_type_by_id / existence checks before invoking config resolution.

Example fix

// before
config = TenantModelService.get_model_config_by_id(tenant_id, LLMType.EMBEDDING, dataset.embd_id)

// after
exist, _ = TenantModelService.get_by_id(dataset.embd_id)
if not exist:
    raise ValueError(f"Embedding model {dataset.embd_id} no longer exists; re-select it on the dataset")
config = TenantModelService.get_model_config_by_id(tenant_id, LLMType.EMBEDDING, dataset.embd_id)
Defensive patterns

Strategy: validation

Validate before calling

exist, model_obj = TenantModelService.get_by_id(model_id)
if not exist:
    raise ValueError(f"Model {model_id} does not exist; re-select it in Model Providers")

Type guard

def model_id_exists(model_id: str) -> bool:
    exist, _ = TenantModelService.get_by_id(model_id)
    return exist

Try / catch

try:
    config = get_model_config_by_id(tenant_id, model_type, model_id)
except LookupError as e:
    # treat as permanent config error: surface to user, do not retry
    raise HTTPException(404, str(e))

Prevention

When it happens

Trigger: Calling get_model_config_by_id(tenant_id, model_type, model_id) with a stale, deleted, or malformed model id — e.g. the tenant_model row was deleted after being referenced by a chat/dataset, or a UUID from another environment was passed, or the id was truncated when copied.

Common situations: Model removed in System/Model Providers while still referenced by a dataset or assistant; DB restored/imported without tenant_model rows; multi-environment config where an id from one DB is used against another.

Related errors


AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15). Data as JSON: /api/errors/76d695e45803f744. Report an issue: GitHub.