mypy(workflow-engine): Prepare for detector.project to be nullable - #120744
Merged
leeandher merged 3 commits intoJul 28, 2026
Merged
Conversation
leeandher
marked this pull request as ready for review
July 28, 2026 15:52
This was referenced Jul 28, 2026
leeandher
force-pushed
the
leanderrodrigues/iswf-3123-typing-fixes-before-migration
branch
from
July 28, 2026 17:04
50406d9 to
1f65dfc
Compare
saponifi3d
approved these changes
Jul 28, 2026
saponifi3d
left a comment
Contributor
There was a problem hiding this comment.
lgtm - just an unrelated note to keep in mind for the future. i also confirmed that there shouldn't be any other call sites. 🚢
This comment was marked as outdated.
This comment was marked as outdated.
saponifi3d
reviewed
Jul 28, 2026
leeandher
force-pushed
the
leanderrodrigues/iswf-3123-typing-fixes-before-migration
branch
from
July 28, 2026 18:01
1f65dfc to
0191043
Compare
Comment on lines
155
to
+156
| def get_org_from_detector(detector: Detector) -> tuple[Organization]: | ||
| return (detector.project.organization,) | ||
| return (detector.linked_project.organization,) |
Contributor
There was a problem hiding this comment.
Cache invalidation crashes for non-project-scoped detectors
get_org_from_detector now uses detector.linked_project, which raises ValueError when project is None. This function is registered as a post_save/post_delete signal handler for all Detector models, so saving or deleting any non-project-scoped detector (introduced by this PR) will propagate an unhandled exception through Django signals.
Evidence
Detector.linked_projectraisesValueError("Detector is not project-scoped")whenself.project is None(src/sentry/workflow_engine/models/detector.py:123-124).@cache_func_for_models([(Detector, get_org_from_detector)])registersget_org_from_detectoras thearg_getterforpost_saveandpost_deletesignals on everyDetectorinstance (src/sentry/utils/function_cache.py:66-97).clear_cache_for_cached_funccallsarg_getter(instance)without any try/except (src/sentry/utils/function_cache.py:47), so theValueErrorpropagates directly.- The PR explicitly introduces detectors with nullable
project("All Projects"), meaning uptime code will now hit this crash path whenever one of those detectors is saved or deleted. - The cached function
get_active_auto_monitor_count_for_orgonly cares about uptime detectors withproject__organization, so aproject=Nonedetector is irrelevant to its cache, but the signal still fires unconditionally.
Identified by Warden · sentry-backend-bugs · 3CL-M2W
leeandher
deleted the
leanderrodrigues/iswf-3123-typing-fixes-before-migration
branch
July 28, 2026 21:40
leeandher
added a commit
that referenced
this pull request
Jul 28, 2026
) The cascade deletions cause issues with these caching functions. The test failures can be [seen in the migration PR](#120687 (comment)). I'm not too familiar with these utilities but from my understanding: - It's causing failures because the cascade deletion order has changed - with the NOT NULL key to Project: the Detector was always deleted before the Project, meaning Detector's post_delete fires, and that Project is still in the DB, so detector.project.organization (on the in-memory Detector) still works. - with the nulllable FK to Project: Django does not ensure that Detector's post_delete happens before the Project row is removed. When the Project is removed first, the caching function hits the DoesNotExist error. I consulted claude about this ordering change, who added: > the reason the ordering changes is specific to how Django's cascade collector works. It tracks dependencies between models to determine deletion order, but it only registers a dependency when the FK is NOT NULL (line 136 of django/db/models/deletion.py: if source is not None and not nullable). And the CASCADE handler passes nullable=field.null (line 27). So making the FK nullable literally removes the ordering constraint that kept Detector-before-Project, and the deletion order becomes arbitrary. Previous PR: #120744 Next PR: #120687
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
sorry for all the codeowners pings 🙏
we're looking to support a new detector for All Projects, but the current model (
Detector) has a FK to Project. We can't use a random project without having issues with things like cascade deletions and project transfers so we're opting to make the column nullable.That will introduce a lot of mypy issues though, so before that migration we're addressing those here.
Tech Spec - https://app.notion.com/p/sentry/Spec-All-Projects-Default-Alert-Migration-8e48b10e4b5d821fb1c581cc65e65c32?source=copy_link
Linear Ticket - https://linear.app/getsentry/issue/ISWF-3123/create-migration-to-support-all-projects