astral-sh/ruff · error

`object` should always be a non-generic class in typeshed

Error message

`object` should always be a non-generic class in typeshed

What it means

`ClassLiteral::object` resolves `builtins.object` from the active `ProgramEnvironment` and asserts it is a plain, non-generic class literal, as typeshed guarantees. The expect fires when the loaded typeshed does not define `object` as a resolvable non-generic class. This is an environment problem (missing/broken/foreign stubs), not something user Python code can cause.

Source

Thrown at crates/ty_python_semantic/src/types/class.rs:631

)]
pub enum ClassLiteral<'db> {
    /// A class defined via a `class` statement.
    Static(StaticClassLiteral<'db>),
    /// A class created dynamically via `type(name, bases, dict)`.
    Dynamic(DynamicClassLiteral<'db>),
    /// A class created via `collections.namedtuple()` or `typing.NamedTuple()`.
    DynamicNamedTuple(DynamicNamedTupleLiteral<'db>),
    /// A class created via functional `TypedDict("Name", {...})`.
    DynamicTypedDict(DynamicTypedDictLiteral<'db>),
    /// A class created via functional enum syntax, e.g., `Enum("Color", "RED GREEN BLUE")`.
    DynamicEnum(DynamicEnumLiteral<'db>),
}

#[salsa::tracked]
impl<'db> ClassLiteral<'db> {
    /// Return a `ClassLiteral` representing the class `builtins.object`
    pub(super) fn object(db: &'db dyn Db, env: &ProgramEnvironment<'db>) -> Self {
        KnownClass::Object
            .to_class_literal(db, env)
            .as_class_literal()
            .expect("`object` should always be a non-generic class in typeshed")
    }

    pub(super) fn recursive_type_normalized_impl(
        self,
        db: &'db dyn Db,
        env: &ProgramEnvironment<'db>,
        div: Type<'db>,
        nested: bool,
    ) -> Option<Self> {
        match self {
            Self::Dynamic(dynamic) => Some(Self::Dynamic(
                dynamic.recursive_type_normalized_impl(db, env, div, nested)?,
            )),
            Self::DynamicNamedTuple(named_tuple) => Some(Self::DynamicNamedTuple(
                named_tuple.recursive_type_normalized_impl(db, env, div, nested)?,

View on GitHub (pinned to 15f3fe6b15)

Solutions

  1. Run ty with its bundled default typeshed (remove any custom typeshed/search-path override) and re-check.
  2. Inspect the typeshed ty loads: `stdlib/builtins.pyi` must contain `class object:` with no type parameters.
  3. Upgrade/downgrade ty so the binary and its vendored typeshed come from the same release.
  4. If a custom typeshed is required, restore a standard typeshed checkout (same version the vendored snapshot is pinned to).

Example fix

# before: custom typeshed where stdlib/builtins.pyi is missing or has
#   class object[T]: ...
# after: stdlib/builtins.pyi must declare the plain class
#   class object:
#       __module__: str
#       ...
Defensive patterns

Strategy: validation

Validate before calling

# before pointing ty at a custom typeshed, verify the stub it will load:
grep -n '^class object' <typeshed>/stdlib/builtins.pyi
# empty output, or a generic `class object[T]`, means this panic is guaranteed

Prevention

When it happens

Trigger: Running checks with a custom, minimal, outdated, or corrupted typeshed whose `stdlib/builtins.pyi` is missing or does not declare a plain `class object:`; mixing a typeshed snapshot from one ty version with the binary of another; a failed/partial extraction of the vendored typeshed.

Common situations: Pointing ty at a custom stub directory; offline/containers builds that strip the bundled typeshed; upgrading ty in place while an old vendored snapshot remains on disk.

Related errors


AI-assisted analysis of astral-sh/ruff@15f3fe6b15 (2026-08-20). Data as JSON: /api/errors/84d879b2c3949f7b. Report an issue: GitHub.