Skip to content

Conversation

jnovinger
Copy link
Member

@jnovinger jnovinger commented Oct 15, 2025

Fixes: #20587

Fixes #20587 (and its duplicate #20588) by adding a null check when handling stale ContentTypes in has_feature().

When running remove_stale_contenttypes (executed by upgrade.sh), the management command fails with TypeError: issubclass() arg 1 must be a class. This occurs when deleting ContentTypes that no longer have corresponding model classes—referred to as "stale" ContentTypes. These arise when models are renamed/removed between versions or when plugins are uninstalled.

The error path:

  1. Django's pre_delete signal fires when deleting the stale ContentType
  2. notify_object_changed() signal handler calls has_feature(instance, 'notifications')
  3. has_feature() calls model_class() on the ContentType, which returns None for stale types
  4. This None gets passed directly to the feature test lambda: issubclass(None, NotificationsMixin) → TypeError

Root Cause:

Commit 5ceb6a6 (which fixed #20290) changed the ContentType code path in has_feature() to use direct feature registry lookups in addition to ObjectType lookups. However, this refactoring inadvertently removed the null safety check that existed in the previous implementation.

https://gist.github.com/jnovinger/35948fc653c0425a5f9b207f9b77883f is available as a reproduction script to verify this fix.

When deleting stale ContentTypes during remove_stale_contenttypes, the
pre_delete signal triggers notify_object_changed(), which calls
has_feature() with the ContentType instance. For stale types (those with
no corresponding model class), model_class() returns None, which then gets
passed to issubclass() in the feature test lambda, causing a TypeError.

The previous implementation in has_feature() checked for None before
attempting ObjectType lookup. The optimization in 5ceb6a6 removed this
safety check when refactoring the ContentType code path to use direct
feature registry lookups. This restores the null check to maintain the
original behavior of returning False for stale ContentTypes.
@jnovinger jnovinger requested review from a team and jeremystretch and removed request for a team October 15, 2025 17:32
Copy link
Member

@jeremystretch jeremystretch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work tracking this down @jnovinger!

@jeremystretch jeremystretch merged commit 2ae98f0 into main Oct 15, 2025
10 checks passed
@jeremystretch jeremystretch deleted the 20587-stale-contenttype-fix branch October 15, 2025 18:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants