Skip to content

Taxon deletion nulls determination.TaxonID outside the delete's transaction — failed/cancelled deletes silently orphan determinations at scale #8434

Description

@urban011

Summary

Deleting a taxon node clears TaxonID and PreferredTaxonID on every determination referencing that node and its descendants, and that clearing survives when the delete itself does not complete. The taxon stays in the tree; its determinations are left blank.

At UT Austin's Vertebrate Paleontology collection (VPL) this silently unlinked 10,829 determinations across 273 taxa in a single working day, during an ordinary taxon-tree cleanup session. It was not noticed for twelve months. Total confirmed impact on this instance is 8,639 determinations across two separate collections (8,631 in VPL, 8 in our non-vertebrate paleo collection over 2021–2025 — see "It is not one collection" below).

This is not related to #8064. Separate bug, separate mechanism.

Environment

  • Specify 7 v7.12.0.7
  • MariaDB 11.4.10
  • Upgrade applied 2025-08-28 (migrations specify.0001_initialworkbench.0008_spdataset_rolledback)
  • Damage began 2025-08-29 08:50 local, the following morning

What we observed

On 2025-08-29, determination rows lost both TaxonID and PreferredTaxonID in ~70–100 discrete bursts of 15–20 rows/second spread through the working day. spauditlog for that day records:

TableNum Action FieldName count
9 (Determination) 1 (update) taxon_id 8,962
9 1 taxon 29
4 (Taxon) 2 (remove) (all fields) 20

The 2022 rows show the failed-delete path is old — two records in four years, against 10,829 in one morning after the upgrade. Our read is that the upgrade removed a guardrail (and shipped the #5324 subtree-delete feature to this instance for the first time) rather than introducing the defect. We have instructed our collection manager to stop deleting taxon nodes entirely.

It is not one collection

We checked all nine Specify schemas on this instance with the same audit-log fingerprint (TableNum 9, FieldName taxon_id/taxon, OldValue set, NewValue empty, determination currently blank). A second collection — NPL, non-vertebrate paleontology, a separate database with a separate taxon tree — had 8 more, scattered from 2021-10-26 to 2025-11-13, all pointing at taxa that still exist. Same signature, different database, four-year span. The other seven schemas were clean.

Total repaired on this instance: 8,639 determinations across two collections.

Possibly related existing issues

Why this is severe

  • Silent. No error, no warning, no count shown to the user. The determination simply goes blank.
  • Undetectable by the user. A collection manager querying "current determination with empty taxon" cannot distinguish these from genuinely undetermined material.
  • Unbounded. One delete attempt on a high-level node takes out every determination beneath it. Equidae alone accounted for 2,000 records, Mammalia 766, Vertebrata 504.
  • Recoverable only by luck. We recovered because auditing was enabled and we still had a monthly dump from four weeks before the incident. An institution with 30-day retention and auditing off would have lost this permanently, and would not have known for a year.

Secondary concern in the same migration

Independent of the bug above: on_delete=CASCADE on taxon.parent means deleting a family node silently deletes every genus and species beneath it. For a taxonomic tree in a collections management system that seems like a dangerous default, particularly with no user-facing confirmation of how many descendants are in scope. Same applies to geography, storage and lithostrat.

Suggested fix

  1. Put the reference-clearing and the delete in one atomic block, so a refused delete rolls the clearing back.
  2. Before clearing anything, show the user how many determinations and descendant nodes the delete will affect, and require confirmation.
  3. Consider refusing outright, as pre-upgrade behaviour did, when determinations reference the node.

What we can supply

  • spauditlog / spauditlogfield extracts for the affected window
  • The full list of 273 affected taxa with per-taxon record counts
  • SHOW CREATE TABLE determination and the full applied-migration list with timestamps
  • A pre-incident and post-incident dump of determination for diffing

Happy to test a fix against a copy of the collection.

How we repaired it

For anyone hitting this before a fix ships. Two independent sources, reconciled before writing:

  1. spauditlogfield.OldValue where TableNum = 9, FieldName IN ('taxon_id','taxon'), OldValue set and NewValue empty, deduplicated to the latest nulling event per determination.
  2. A pre-incident mysqldump, loaded into a scratch schema and joined on DeterminationID.

The two agreed on all 8,574 records they shared, with zero conflicts. PreferredTaxonID was recomputed as COALESCE(taxon.AcceptedID, taxon.TaxonID), a rule validated against 8,574 historical values before use. Post-restore, preferred-taxon consistency across the entire determination table checks clean.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions