Auto Loader Schema Inference and Evolution: Modes, Hints and the Rescued Data Column
Databricks DE Associate · Deep dive

Auto Loader Schema Inference and Evolution: Modes, Hints and the Rescued Data Column

Databricks Auto Loader infers a schema by sampling the first files it reads, stores it in cloudFiles.schemaLocation, and decides what to do when a new column arrives through cloudFiles.schemaEvolutionMode. The default, addNewColumns, records the new column and stops the stream once so that a restart picks it up.

Last updated October 2026.

What this deep dive adds

Chapter 7 of the course covers what Auto Loader is: the cloudFiles source, file discovery, the checkpoint and the idea of schema evolution. This post is the reference behind that card: a full pipeline, every option it uses, the five evolution modes side by side, and what lands in _rescued_data.

Auto Loader sits in Data Ingestion and Loading, the second of the seven sections in the May 2026 version of the Databricks Certified Data Engineer Associate exam. Questions usually describe a pipeline that broke, or did not break, when the files changed shape.

The full pipeline, line by line

checkpoint = "/Volumes/main/bronze/checkpoints/events"

(spark.readStream
    .format("cloudFiles")
    .option("cloudFiles.format", "json")
    .option("cloudFiles.schemaLocation", checkpoint)
    .option("cloudFiles.inferColumnTypes", "true")
    .option("cloudFiles.schemaHints", "event_ts timestamp, amount decimal(10,2)")
    .load("/Volumes/main/landing/events/")
  .writeStream
    .option("checkpointLocation", checkpoint)
    .option("mergeSchema", "true")
    .trigger(availableNow=True)
    .toTable("main.bronze.events"))
LineWhat it does
format("cloudFiles")Selects Auto Loader as the streaming source.
cloudFiles.formatThe file type being read: json, csv, xml, parquet, avro, text, binaryFile and others.
cloudFiles.schemaLocationWhere the inferred schema, and every later version of it, is stored. It can be the same path as the checkpoint. Without it, inference and evolution are off.
cloudFiles.inferColumnTypesInfers real types for JSON, CSV and XML. Without it every column comes back as a string.
cloudFiles.schemaHintsPins the types of columns you already know, in SQL schema syntax.
checkpointLocationRecords which files were processed, so a restart resumes exactly where it stopped.
mergeSchemaLets the Delta target accept the columns Auto Loader adds. The Databricks schema evolution examples pair it with Auto Loader on the write.
trigger(availableNow=True)Processes every file that is waiting, then stops: batch scheduling with streaming bookkeeping.

Step 1: how the schema is inferred

On the first run Auto Loader samples up to 50 GB or 1,000 files, whichever limit it reaches first, and writes the result to a _schemas directory under the schema location. Later runs read the stored schema instead of sampling again, which is why the schema location is required.

Step 2: schema hints for the columns you know

.option("cloudFiles.schemaHints", "tags map<string,string>, version int")

A hint overrides the inferred type of one column and leaves the rest to inference. Hints can also name a column that has not appeared yet, so it exists with the right type from the first batch. They apply only when you have not supplied a full schema with .schema(...), and they work with inferColumnTypes on or off.

Step 3: a new column arrives

This is the part the exam tests. cloudFiles.schemaEvolutionMode takes one of five values:

ModeOn a new columnStream
addNewColumnsAdds it to the end of the schema in the schema location. Existing column types never change.Stops once with UnknownFieldException; the restart uses the new schema. Default when no schema is supplied.
addNewColumnsWithTypeWideningAs addNewColumns, and also widens supported types (int to long). Unsupported changes, such as int to string, go to the rescued data column.Stops once, like addNewColumns. Public Preview on Databricks Runtime 16.4 and above.
rescueNever changes the schema. The new column's values go to the rescued data column.Keeps running.
failOnNewColumnsChanges nothing.Fails and stays failed until you update the schema or remove the file.
noneIgnores the new column. Its data is dropped unless rescuedDataColumn is set.Keeps running. Default when you supply a schema.

Sourced from the Databricks documentation page "Configure schema inference and evolution in Auto Loader".

Why the default stops the stream on purpose

With addNewColumns, Auto Loader runs inference on the latest micro-batch, appends the new columns to the stored schema and then fails the stream. A running stream cannot change its schema in place, so the failure is how the new version takes effect. Databricks recommends running the stream as a Lakeflow Job, whose retry restarts it without anyone stepping in. A question that says "the stream failed after the files gained a field, and succeeded on retry" is describing exactly this.

What addNewColumns does when a new field arrives Run 1 reads files with id and amount under schema v0. A file adds a discount field. Auto Loader appends discount to the stored schema and fails the run with UnknownFieldException. The job retry starts run 2 with schema v1: id, amount, discount. 1 · RUN 1 Files arrive with id and amount schema v0: id, amount 2 · A NEW FIELD ARRIVES One file also carries discount {"id":7, "amount":12, "discount":2} 3 · SCHEMA SAVED, RUN STOPS Auto Loader appends discount to the schema in _schemas, then fails the run UnknownFieldException 4 · JOB RETRY: RUN 2 The restart reads with the new schema schema v1: id, amount, discount
With the default addNewColumns mode, one stopped run is how a new column takes effect.

Supplying your own schema

(spark.readStream
    .format("cloudFiles")
    .option("cloudFiles.format", "json")
    .schema("id bigint, event_ts timestamp, amount decimal(10,2)")
    .option("rescuedDataColumn", "_rescued_data")
    .load("/Volumes/main/landing/events/"))

With .schema(...) the default mode becomes none, and addNewColumns is not allowed at all. The line above adds the rescued data column explicitly, so unexpected fields are kept rather than silently dropped. If you want evolution with known types, use schema hints instead of a full schema.

The rescued data column

When inference is on, Auto Loader adds a _rescued_data column. Instead of failing or dropping a record that does not fit the schema, it stores the parts that did not fit as a JSON string, together with the path of the file they came from. Three things end up there:

Where a record that does not fit the schema ends up An incoming JSON record has id 9, amount "twelve" and coupon "X1", against a schema of id BIGINT and amount INT in rescue mode. The row written has id 9, amount NULL, and _rescued_data holding the amount and coupon values plus the source file path. INCOMING JSON RECORD {"id": 9, "amount": "twelve", "coupon": "X1"} Schema: id BIGINT, amount INT · mode: rescue ROW WRITTEN TO THE TABLE id amount _rescued_data 9 NULL {"amount": "twelve", "coupon": "X1", ...} amount: wrong type, so NULL here, original kept coupon: not in the schema, kept, not dropped The JSON also records the source file path
A type mismatch and an unknown field, kept in _rescued_data instead of failing the read.
SELECT _rescued_data, count(*) AS records
FROM main.bronze.events
WHERE _rescued_data IS NOT NULL
GROUP BY _rescued_data;

A query like this in Silver is the usual way to notice drift without stopping Bronze. The column also changes how parser modes behave: with it set, a type mismatch no longer drops a record in DROPMALFORMED or fails the read in FAILFAST. Only records that are actually corrupt do.

Two details that are easy to miss

What this means for the exam

Exam tip: "absorb new columns" points at the default addNewColumns, and "the stream stopped once, then ran" is that mode working as designed. "Never stop, keep unexpected fields for later" is rescue. "Fail loudly on any change" is failOnNewColumns. "A schema was supplied and the new field vanished" is none, the default with a supplied schema.

A common distractor puts schema handling on the checkpoint or on file notification mode. The checkpoint tracks which files were processed, and file notification only changes how new files are found. Neither touches the schema. When to choose Auto Loader over COPY INTO at all is covered in the COPY INTO vs Auto Loader post and in Chapter 8 of the course.

FAQ

What is the default schema evolution mode in Auto Loader?

The default is addNewColumns when you do not supply a schema, and none when you do. With addNewColumns the stream stops once on a new column, records it in the schema location, and picks it up on restart.

Why did my Auto Loader stream fail with UnknownFieldException?

That failure is how addNewColumns applies a new column: Auto Loader has already saved the updated schema, and the next run uses it. Run the stream as a Lakeflow Job with retries so the restart happens automatically.

What goes into the _rescued_data column?

Data that did not fit the schema: fields missing from it, values with the wrong type, and fields whose case differs. It is stored as a JSON string with the source file path, so nothing is silently lost.

Why does Auto Loader infer every JSON column as a string?

Strings never fail to parse, so a value that changes type between files cannot break the stream. Set cloudFiles.inferColumnTypes to true for real types, or pin specific columns with cloudFiles.schemaHints.

Back to the chapter

Auto Loader is Chapter 7 of the Databricks Data Engineer Associate course, in Unit 2, Data Ingestion and Loading, with the source, discovery and checkpoint built up card by card and exam-style checks.

Open Chapter 7 →