BerriAI/litellm · error · ValueError

Cannot create domain model from None record

Error message

Cannot create domain model from None record

What it means

DomainModel.from_db_record() converts a database row (dict, pydantic model with model_dump()/dict(), or any row object) into the domain model. Passing None — typically a 'not found' query result — raises ValueError immediately instead of producing a half-built object.

Source

Thrown at litellm/models/base.py:27


class DomainModel(BaseModel):
    """Base class for all domain models."""

    model_config = ConfigDict(
        from_attributes=True,
        protected_namespaces=(),
        extra="ignore",
    )

    created_at: datetime | None = None
    updated_at: datetime | None = None

    @classmethod
    def from_db_record(cls, record: Any) -> "DomainModel":
        """Create a domain model from a database record."""
        if record is None:
            raise ValueError("Cannot create domain model from None record")
        if isinstance(record, dict):
            return cls(**record)
        if hasattr(record, "model_dump") and callable(record.model_dump):
            return cls(**record.model_dump())
        if hasattr(record, "dict") and callable(record.dict):
            return cls(**record.dict())
        return cls(**dict(record))

    def to_db_dict(self, exclude_unset: bool = False) -> dict[str, Any]:
        """Convert domain model to a dictionary for database operations."""
        return self.model_dump(exclude_none=True, exclude_unset=exclude_unset)

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Check for None first and translate it into a domain-level not-found error
  2. Use a repository method that raises a typed DoesNotExist exception when the query misses
  3. If the row should exist, fix the query (id/tenant filter) rather than the mapper

Example fix

# before
user = User.from_db_record(users_table.get_user(user_id))

# after
record = users_table.get_user(user_id)
if record is None:
    raise LookupError(f"user {user_id} not found")
user = User.from_db_record(record)
Defensive patterns

Strategy: type-guard

Validate before calling

record = repo.get_user(user_id)
if record is None:
    raise LookupError(f"user {user_id} not found")
user = User.from_db_record(record)

Type guard

def is_record(record: object) -> bool:
    return record is not None and (isinstance(record, dict) or hasattr(record, "model_dump") or hasattr(record, "dict") or hasattr(record, "__iter__"))

Try / catch

try:
    user = User.from_db_record(record)
except ValueError as e:
    if "None record" in str(e):
        raise LookupError("requested row does not exist") from e
    raise

Prevention

When it happens

Trigger: A repository getter returns None (no matching row) and the caller immediately does User.from_db_record(record); read-after-delete flows; queries with overly tight filters (wrong tenant/id).

Common situations: Service layer with no not-found handling; race where the row is deleted between check and read; tests with empty fixtures; soft-deleted rows excluded by the query.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/9aff1be46db92366. Report an issue: GitHub.