> ## Content Index
> Fetch the complete content index at: https://debugly.dev/llms.txt
> Use this file to discover other available public pages before exploring further.

# The Training Export That Included Every Tenant's Rows
- URL: https://debugly.dev/fine-tune-export-leaked-tenants/
- Published: 2026-10-09T14:55:00.000Z
- Updated: 2026-10-17T05:56:12.000Z
- Description: The dataset query had no tenant filter, so the fine-tuning set contained other customers' records, and the model learned them, and the model answered them…
- Author: Rohit Bhadani
- Tags: Security, AI, Data

I started at the query, because that is where the symptom landed, and walked it backwards link by link until the never entered stopped looking innocent.

The unfiltered training export has a single weak property, and here it is: the data is not copied somewhere it can be deleted, it is learned into the weights, and the learned cannot be removed by deleting the file.

The case was a multi-tenant SaaS fine-tuning a support model; the mechanics generalise to any shared dataset built from a shared database.

## Why the filter was missing

The export was written by the data team, and the data team's queries run in the warehouse, and the warehouse is the analytics context, and the analytics context is the cross-tenant, because the analytics aggregate across customers, and the across is the normal, and the normal is the assumption, and the assumption was carried into the export, and the export was the training set, and the training set was the model's input, and the input is not the analytics, and the not analytics is the difference, and the difference is the boundary, and the boundary was missing, and the missing was the breach.

The filter was missing because the export was written as an analytics query, and the analytics query is the cross-tenant, and the cross-tenant is correct for the dashboard and wrong for the training set, and the wrong is the context, and the context is the difference, and the difference is the boundary, and the boundary is the filter, and the filter is the fix, and the fix is the one line, and the one line was the breach's absence.

The missing filter survived review because the reviewer read the query as an export, and the export is the data movement, and the movement is the normal, and the normal is the pass, and the pass is the breach, and the breach is the reason the review should ask whose data this is, and the whose is the boundary, and the boundary is the question the query does not answer.

## Why the learned data cannot be deleted

The export was deleted, and the deleted was the file, and the file was the copy, and the copy was the removable, and the removable was the easy part, and the easy part was not the problem, because the model was trained, and the trained was the weights, and the weights are the data, and the data is the learned, and the learned is the not removable, and the not removable is the breach's persistence.

The model can reproduce the learned records, and the reproduce is the answer, and the answer is the disclosure, and the disclosure is the breach, and the breach persists because the weights persist, and the weights are the model, and the model is the deployment, and the deployment is the service, and the service is the exposure, and the exposure is the every request, and the every request is the breach's scale.

The only reliable remediation is retraining without the contaminated data, and the retraining is the cost, and the cost is the time, and the time is the exposure's duration, and the duration is the breach, and the breach is the reason the filter matters before the training, because after the training the data is in the weights, and the weights are the irreversible.

This is the same irreversibility as [the secrets in git history that are still there](https://debugly.dev/secrets-in-git-history-are-still-there/), and the shared property is the one worth fixing.

## What actually fixed it

**Filter the export by tenant.** The query has the tenant identifier in the where clause, and the clause is the boundary, and the boundary is the fix, and the fix is the one line, and the one line is the prevention, and the prevention is the discipline, because the training set is one tenant's data and the query must say so.

**Validate the export before training.** The dataset is checked for cross-tenant records, and the checked is the gate, and the gate is the block, and the block is the prevention, and the prevention is the validation, and the validation is the count, and the count is the distinct tenant identifiers, and the identifiers should be one, and the more than one is the failure, and the failure is the catch.

**Treat the training set as a data boundary.** The export is a trust boundary crossing, and the crossing requires the review, and the review is the gate, and the gate is the approval, and the approval is the discipline, and the discipline is the prevention, because the training set leaves the database and enters the weights, and the entering is the irreversible.

**Log the export's provenance.** The dataset records the query, the time, and the row count, and the recorded is the audit, and the audit is the traceability, and the traceability is the diagnosis, and the diagnosis is the fix, and the fix is the record, and the record is the incident's first question answered, and the answered is the speed.

**Retrain on a clean set.** The contaminated model is retired, and the retired is the exposure's end, and the end is the remediation, and the remediation is the retraining, and the retraining is the clean set, and the clean set is the filtered export, and the filtered is the fix, and the fix is the cost, and the cost is the reason to filter first.

## The rule

A training export built from a multi-tenant database needs an explicit tenant filter, because the learned data enters the weights and cannot be deleted by removing the file. Filter the query, validate the dataset for cross-tenant records before training, treat the export as a trust boundary, and log the provenance.

The model recited a customer's invoice number to a different customer, because the export query had no tenant filter and the training set contained everyone's rows, and the training made the leak permanent. I now check for that property first, because it is where the failure actually lives.

---

Published on [debugly.dev](https://debugly.dev/) by [Rohit Bhadani](https://debugly.dev/about/) | Founder of [Krova Cloud](https://krova.cloud/?ref=debugly.dev)