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)

2 public reports · verified 2026-10-21

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

  1. 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]
  2. 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]
  3. 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. [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. [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 classStageLimitationBasis
Integration testspre-deployAn 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 testspre-mergeA 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)
Counts behind the grades
Accepted public reports2
Published in the last 5 years2
Impact stated: availability1
Impact stated: data1

How the grades are computed

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

Providers whose site names these test classes

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].

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.