Monitor the rescue column
One query reveals rows whose unknown, mismatched, or mis-cased fields were moved while the load stayed green.
See the monitor →_rescued_dataAuto Loader can move unknown columns, type mismatches, and case mismatches into _rescued_data while the load stays green. The nulls do not mean the values were missing. They mean the values were moved.
Rescued fields are not missing data. They are data that failed the declared shape and were moved into a JSON blob. Detect them in bronze, triage them by field, and choose whether to promote or quarantine them before silver.
One query reveals rows whose unknown, mismatched, or mis-cased fields were moved while the load stayed green.
See the monitor →Two deterministic input rows become two output rows — one clean and one rescued — without turning the read red.
Run the demo →Malformed records follow parser mode or bad-record routing. Type mismatches can stay green and move into _rescued_data.
Last verified against official Databricks docs: October 13, 2026. Corrections: none yet — first published as Issue 025, October 13, 2026.
Auto Loader's rescued data column quietly quarantines anything the schema can't parse — unknown columns, type mismatches, case mismatches — into _rescued_data as JSON, and the load stays green. The nulls don't mean the values were missing. They mean the values were moved.
The one-line monitor that belongs on every bronze table you ingest with Auto Loader:
SELECT * FROM bronze_events WHERE _rescued_data IS NOT NULL
If that query returns rows, your load didn't fail — it buried the evidence. The JSON blob also carries the source file path, so you can trace every rescued record back to the exact file that carried it.
rescuedDataColumn option, you get no rescue column at all — malformed data then behaves however your parser mode says (dropped or failed), and the quiet quarantine you were counting on never existed.Not an observed production result: the setup writes exactly two JSON records. The row counts and outcomes below follow deterministically from those inputs and the declared schema.
dbutils.fs.rm("dbfs:/tmp/demo_rescue", True)
dbutils.fs.put("dbfs:/tmp/demo_rescue/clean.json", '{"id": 1, "amount": 100}\n', True)
dbutils.fs.put("dbfs:/tmp/demo_rescue/dirty.json", '{"id": 2, "amount": "one hundred"}\n', True)
bronze = (spark.read.schema("id INT, amount INT").option("rescuedDataColumn", "_rescued_data").json("dbfs:/tmp/demo_rescue"))
bronze.createOrReplaceTempView("demo_bronze")
SELECT id,
amount,
_rescued_data IS NOT NULL AS was_rescued
FROM demo_bronze
ORDER BY id;
id=1, amount=100, was_rescued=false. Row 2: id=2, amount=NULL, was_rescued=true — the "one hundred" string did not become the amount; it was moved into the _rescued_data JSON blob (which also records the source file path), and amount was set to NULL. The read reported success. Two rows in, two rows out — and one of them is lying.Routing, per official docs:
_rescued_data JSON; the load stays green._corrupt_record option, or badRecordsPath.numSkippedCorruptFiles.badRecordsPath only.rescuedDataColumn set explicitly, even with a declared schema._rescued_data IS NOT NULL per batch, and alert — do not merely log.get_json_object(_rescued_data, '$.<field>'). Promote it by fixing the upstream producer or adding the column via addNewColumns evolution and reprocessing the file; or quarantine it by routing to a DQ table and holding it from silver.addNewColumns fails the stream on a new column and evolves on restart; rescue mode never evolves — new columns park in the rescue column forever. Pick deliberately; rescue is not a schema-evolution strategy.SELECT COUNT(*) AS total_rows,
SUM(CASE WHEN _rescued_data IS NOT NULL THEN 1 ELSE 0 END) AS rescued_rows
FROM bronze_events;
SELECT get_json_object(_rescued_data, '$.amount') AS rescued_amount,
_rescued_data AS rescue_blob
FROM bronze_events
WHERE _rescued_data IS NOT NULL;
Because there are two different kinds of broken, and they behave differently. Broken syntax — a truncated line or an unclosed quote — is genuinely malformed: with FAILFAST it errors, with DROPMALFORMED it drops, and with badRecordsPath it gets written there.
But broken types — a string where the schema says INT — and unknown columns are not “malformed” to the parser. With a rescue column configured, they are quarantined into _rescued_data while the load sails on green. Your “garbage” records arrived as rows with NULLs where the bad values were, plus a JSON blob holding the originals.
So: syntax breaks the load; types quietly move house. If you want the stream to fail on bad values, that is a deliberate configuration — explicit schema, no rescue column, strict parser options — not the default.
Practitioner note — not official documentation: a Databricks Community technical blog recommends pairing _rescued_data with the legacy _corrupt_record option to cover both failure classes in one bronze.
FAILFAST so the deviation wakes someone up.cloudFiles) on JSON/CSV; any compute; Delta bronze tables.cloudFiles), JSON/CSV ingestion, Delta bronze.Structured practice for Databricks and data engineering workflows.
Courses focused on Databricks concepts and workflows.
Browse DataCamp Databricks courses →Structured practice for production data pipelines.
Browse DataCamp data engineering courses →Partner links: we may earn a commission if you enroll through these recommendations, at no extra cost to you.