Soft-delete flag ignored: symptoms, cause, and the test that catches it
A new code path ignores a soft-delete flag and exposes or modifies deleted records.
Soft-delete flag ignored is documented by 2 accepted public reports from 2 organisations, 2 of them published in the last 5 years.
- Likelihood: low (2 public reports in 5 years)
- Impact: high (1 of 2 reports: data)
Symptoms
User-visible
- More than 50,000 user accounts that were already deactivated moved back to pending deletion status and were sent notification emails.[1]
- Email sending for the AP1 region was unavailable from about 2025-08-27 18:46 UTC to 2025-08-28 16:35 UTC.[1]
- After a framework upgrade from major version 8 to 9, relationship queries built with toBase() returned soft-deleted records that version 8 excluded.[2]
Monitoring-visible
- A large batch of notification emails sent to deactivated account addresses bounced.[1]
- The email service provider temporarily disabled the AP1 sending domain for poor mailing list quality.[1]
- Dumped SQL for a relationship query built with toBase() lacked the deleted_at IS NULL condition, while the same relationship queried directly or through getQuery() still had it.[2]
- The fault surfaced when a user upgrading the framework compared generated SQL between versions 8 and 9 and filed a reproduction.[2]
Root causes
- stage: code A bug in a trial expiration escalation job, enabled on 2025-08-04, moved accounts that a deletion batch had already set to deactivated back to pending deletion, which triggered notification emails to those accounts.[1]
- stage: code In framework version 9, the relationship toBase() path returned the underlying query without applying the model's global scopes, so the soft-delete condition was missing.[2]
- stage: third-party The behaviour change arrived through the framework major version upgrade, so application code that excluded soft-deleted rows on version 8 began returning them on version 9.[2]
Public examples
- [1]Storj, 2025-09-19. A newly enabled trial expiration escalation job moved more than 50,000 already deactivated user accounts back to pending deletion status and sent notification emails to their deactivated addresses, which bounced and led the email provider to disable the AP1 sending domain. primary Storj DCS Status - Email sending issue on AP1
- [2]Laravel, 2022-04-08. After the upgrade from Laravel 8 to Laravel 9 (reported on 9.7.0), calling toBase() on an Eloquent relationship dropped the SoftDeletes deleted_at IS NULL condition, so relationship queries built this way included soft-deleted rows until a maintainer fix applied the scopes in toBase(). primary Calling toBase() on relationships removes soft-deleted condition in Laravel 9 compared to Laravel 8 · Issue #41885 · laravel/framework · GitHub
Test classes that catch it
| Test class | Stage | Limitation | Basis |
|---|---|---|---|
| Integration tests | pre-deploy | An integration test that checks only the default relationship query passes, because that path and getQuery() kept the deleted_at IS NULL condition and only toBase() lost it. | by design analysis |
| Unit tests | pre-merge | A unit test of the trial expiration escalation job starts only from the account states the test sets up, so it misses accounts that a separate deletion batch had already set to deactivated in production. | by design analysis |
Risk
- Likelihood: low (2 public reports in 5 years)
- Impact: high (1 of 2 reports: data)
| Accepted public reports | 2 |
|---|---|
| Published in the last 5 years | 2 |
| Impact stated: availability | 1 |
| Impact stated: data | 1 |
Pre-release checklist
- PR-01
- PR-02
- PR-03
- PR-04
- PR-05
- PR-06
- PR-07
First 30 minutes
- FM-01
- FM-02
- FM-03
- FM-04
- FM-05
- FM-06
- FM-07
Download the checklist
What catches this: catalogue entries
Listed because their own documentation or site names integration tests, unit tests on the verification date. Alphabetical. Not a recommendation.
Tools whose documentation names these test classes
- Hoverfly integration source verified 2026-10-21
- Jest unit source verified 2026-10-21
- JUnit unit source verified 2026-10-21
- Nightwatch unit source verified 2026-10-21
- Postman integration source verified 2026-10-21
- pytest unit source verified 2026-10-21
- RSpec unit source verified 2026-10-21
- Serenity BDD integration source verified 2026-10-21
- Testcontainers integration source verified 2026-10-21
- TestNG unit source verified 2026-10-21 integration source verified 2026-10-21
- Vitest unit source verified 2026-10-21
- WebdriverIO unit source verified 2026-10-21
- XCTest unit source verified 2026-10-21
Providers whose site names these test classes
- andagon people GmbH integration source verified 2026-10-21 unit source verified 2026-10-21
- Appmatics integration source verified 2026-10-21
- AQA Masters integration source verified 2026-10-21
- Cleverix integration source verified 2026-10-21
- Crestech Software Systems integration source verified 2026-10-21
- DeviQA integration source verified 2026-10-21
- Inmetrics unit source verified 2026-10-21
- Invensis Technologies integration source verified 2026-10-21
- Outpost QA integration source verified 2026-10-21
- Q-Centric GmbH integration source verified 2026-10-21
- QA Madness integration source verified 2026-10-21
- QA Mentor integration source verified 2026-10-21 unit source verified 2026-10-21
- QAwerk integration source verified 2026-10-21
- Quality Island integration source verified 2026-10-21
- Soflab Technology integration source verified 2026-10-21
- SQA - Software Quality Assurance S.A. integration source verified 2026-10-21
- SunTec India integration source verified 2026-10-21
- T2S Tecnologia unit source verified 2026-10-21
- TestFort integration source verified 2026-10-21
- Testhouse integration source verified 2026-10-21
- Testing IT integration source verified 2026-10-21
- TestMatick integration source verified 2026-10-21
- Testscenario integration source verified 2026-10-21
- Zoonou integration source verified 2026-10-21
Only on this page
Soft-delete flag ignored starts at the code stage in report [1] and report [2], not in a migration. Neither report describes a schema change, so a migration test has nothing to run against. In report [1], a trial expiration escalation job enabled on 2025-08-04 moved more than 50,000 accounts that a deletion batch had already deactivated back to pending deletion.
In report [2], a framework upgrade from version 8 to 9 dropped the deleted_at IS NULL condition only on relationship queries built with toBase(). The reporter found it by comparing generated SQL between the two versions [2]. The fix proposed by a maintainer applied the scopes inside toBase() before returning the query [2].
Related incidents
- ORM upgrade changes query semantics An ORM or driver upgrade changes default query behaviour and returns different rows.
- Connection pool exhausted A new code path leaks or holds connections and the pool runs out under load.
- Email or SMS provider template broken Transactional messages are sent with broken templates or to wrong recipients after a template change.
- Encoding or timezone shift Stored timestamps or strings are shifted or corrupted after a driver, locale or timezone change.
- Provider API change breaks contract A third-party API changes a field or behaviour and the integration fails.
Questions
What causes soft-delete flag ignored?
In report [1], a new trial expiration escalation job moved already deactivated accounts back to pending deletion. In report [2], a framework upgrade made toBase() on relationships skip the soft-delete scope.
What are the symptoms of soft-delete flag ignored?
In report [1], notification emails to deactivated addresses bounced and the email provider disabled the AP1 sending domain. In report [2], relationship queries returned soft-deleted records that the previous version excluded.
Why did tests not catch soft-delete flag ignored?
In report [2], the relationship query and getQuery() kept the deleted_at IS NULL condition, so only a test of the toBase() path shows the fault. A unit test with a mocked data layer never sees that SQL.
Which test class catches soft-delete flag ignored, and what does it miss?
Integration tests and unit tests carry the by-design label, because neither report names a test class. An integration test that checks only the default relationship query misses the toBase() path.
What should be checked before a release to prevent soft-delete flag ignored?
Item PR-01 seeds a soft-deleted row and confirms every relationship query path excludes it. Item PR-05 asserts that the escalation job leaves an already deactivated account deactivated.
Should a team roll back or fix forward when soft-delete flag ignored happens?
In report [1], the trial expiration escalation job was disabled to investigate, then the root cause was fixed. In report [2], the reporter confirmed the proposed toBase() change fixed a complex query.
Release Incident Playbook (2026). Soft-delete flag ignored: symptoms, cause, and the test that catches it, content version 1.0.0. https://releasefailures.com/incidents/soft-delete-flag-ignored/. Accessed 2026-10-21.