From c8171fac568e741f59e48d586c28da2fe5523914 Mon Sep 17 00:00:00 2001 From: Jason Novinger Date: Wed, 15 Oct 2025 12:27:14 -0500 Subject: [PATCH] Fixes #20587: Handle stale ContentTypes in has_feature() 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. --- netbox/netbox/models/features.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/netbox/netbox/models/features.py b/netbox/netbox/models/features.py index be58d647a6..e0d03d6e78 100644 --- a/netbox/netbox/models/features.py +++ b/netbox/netbox/models/features.py @@ -676,6 +676,8 @@ def has_feature(model_or_ct, feature): # If a ContentType was passed, resolve its model class and run the associated feature test elif type(model_or_ct) is ContentType: model = model_or_ct.model_class() + if model is None: # Stale content type + return False try: test_func = registry['model_features'][feature] except KeyError: