11.3 Troubleshooting Pipeline & Job Failures

Key Takeaways

  • Driver OutOfMemoryError (OOM) usually occurs due to large `collect()` operations or massive broadcast variables exceeding the driver's memory capacity.
  • Executor OutOfMemoryError typically happens during heavy shuffles, data skew, or when processing excessively large partitions without adequate executor memory.
  • ConcurrentAppendException and ConcurrentTransactionException occur in Delta Lake when multiple jobs attempt to modify the same table partition or metadata simultaneously without isolation.
  • Py4JJavaError is a generic wrapper in PySpark; engineers must inspect the nested Java stack trace to identify the actual underlying JVM failure (e.g., FileNotFound, NullPointer).
  • NotSerializableException happens when code attempts to pass an un-serializable object (like a database connection or a non-serializable Python class) from the driver to the executors.
Last updated: July 2026

Data engineers spend a significant portion of their time debugging failed jobs and broken pipelines. The Databricks Certified Data Engineer Professional exam expects you to quickly identify the root cause of standard Spark, Delta Lake, and infrastructure exceptions, and know exactly how to resolve them. Troubleshooting distributed systems is complex because failures can happen at various layers: the network, the disk, the JVM, or the application code itself. Understanding how to interpret error messages and stack traces is a vital skill.

OutOfMemory (OOM) Exceptions

Memory issues are the most frequent cause of Spark job failures. However, Apache Spark is a distributed system, meaning memory exhaustion can happen in two entirely different places: the Driver or the Executors. Knowing the difference is critical to applying the correct fix. Increasing cluster memory blindly is an expensive anti-pattern.

Driver OOM

Symptoms: The job fails quickly, often before significant cluster computation occurs, or immediately at the end of a job. The error reads java.lang.OutOfMemoryError: Java heap space on the driver node. The Spark UI may show that tasks haven't even started, or they finished but the job hung at the very end.

Common Causes:

  1. Calling df.collect() or df.toPandas() on a massive DataFrame, which forces all distributed data back into the single driver node's memory. The driver is not designed to hold gigabytes of raw data.
  2. Broadcasting a dataset that is too large (exceeding spark.sql.autoBroadcastJoinThreshold limits severely). Spark attempts to serialize the massive table on the driver to send to executors, crashing the JVM.
  3. Generating an excessively complex query plan that overwhelms the Catalyst optimizer. This often happens with highly nested SQL queries or dynamic generation of thousands of DataFrame .withColumn transformations.

Resolution: Avoid collect() in production code. Use display() with limits for debugging. If you must bring data to the driver, increase the driver node type memory or increase spark.driver.memory. If broadcasting is the issue, disable auto-broadcast joins or ensure the broadcasted table is heavily filtered.

Executor OOM

Symptoms: Tasks fail repeatedly, executors are marked as lost, and the Spark UI shows ExecutorLostFailure or java.lang.OutOfMemoryError on worker nodes. The DAG execution halts midway through a complex stage.

Common Causes:

  1. Data Skew: One partition contains vastly more data than others, overwhelming a single executor (e.g., grouping by a null key or a highly skewed ID).
  2. Heavy Shuffles: Operations like join, groupBy, or window forcing massive data exchange across the network. If a single shuffle block exceeds memory, the executor crashes.
  3. UDFs: Inefficient Python User Defined Functions (UDFs) that consume excessive memory per row. Since Python runs outside the JVM, standard PySpark UDFs have massive serialization overhead.

Resolution:

  • Fix data skew using salting techniques (appending random numbers to skewed keys to distribute them).
  • Increase the number of partitions (spark.sql.shuffle.partitions default is 200, which is often too small for massive datasets).
  • Switch to a memory-optimized cluster instance type (e.g., AWS r5, Azure E-series).
  • Use vectorized Pandas UDFs instead of standard Python UDFs, which process data in batches rather than row-by-row.

Delta Lake Transaction Conflicts

Delta Lake provides ACID guarantees, which means it protects data integrity when concurrent writes occur. However, this protection surfaces as specific exceptions when conflicts cannot be automatically resolved by Delta's optimistic concurrency control.

ExceptionCauseResolution
ConcurrentAppendExceptionTwo jobs attempt to append files to the same table directory simultaneously, and their file metadata conflicts.Implement automated retry logic, or structure jobs so appends target different partitions concurrently. Delta Lake handles concurrent appends well unless they modify identical metadata files.
ConcurrentTransactionExceptionMultiple streams or batch jobs attempt to update/delete/merge the same data concurrently.Ensure only one writer modifies a specific subset of data at a time. Use job dependencies to serialize merge operations, or leverage Delta's row-level concurrency (if available in the Databricks runtime).
MetadataFetchExceptionA job runs for so long that the underlying Delta log state it read at the start has been purged or heavily modified by other jobs.Shorten the job duration, or ensure compaction/vacuum jobs are not overly aggressive on active tables.

When Delta conflicts occur, reading the exact error message is crucial. Databricks often provides explicit instructions in the exception text detailing exactly which files conflicted and which operations were involved.

Spark Execution and Code Errors

Py4JJavaError

When working in PySpark, you interact with a Python wrapper around a Java/Scala engine. When a JVM error occurs, PySpark wraps it in a Py4JJavaError.

Important: The phrase "Py4JJavaError" is meaningless on its own. It simply means "Java threw an error." To debug it, you must scroll down the stack trace and find the Caused by: line. For example, it might be caused by an AnalysisException (column not found), a java.io.FileNotFoundException (invalid path), or a java.lang.IllegalArgumentException (invalid configuration). Never copy-paste just "Py4JJavaError" into a search engine; always look for the underlying cause.

NotSerializableException

Spark operates by serializing code and data on the driver and sending it over the network to executors.

The Error: org.apache.spark.SparkException: Task not serializable

The Cause: You instantiated an object on the driver that cannot be serialized (turned into bytes), and then tried to use it inside a distributed operation like .map() or a UDF. Common culprits include database connection objects, un-serializable Python classes, file handles, or thread locks. Spark cannot package an active network connection and send it to another computer.

Resolution: Initialize un-serializable objects inside the executor. Use mapPartitions() to open a database connection once per partition on the worker node, rather than opening it on the driver and trying to pass it. This ensures the connection is established locally on the executor executing the task.

TaskKilled Exceptions and Network Timeouts

Symptoms: Tasks are marked as TaskKilled or fail with java.util.concurrent.TimeoutException.

Causes:

  • Spot Instance Preemption: If you use spot/preemptible instances, the cloud provider may reclaim the node with little warning, killing all tasks running on it.
  • Network Timeouts: The executor became unresponsive due to heavy Garbage Collection (GC) pauses caused by near-OOM conditions. If the JVM pauses for minutes to run GC, the driver assumes the executor died and kills its tasks.

Resolution: Spark is resilient; it will retry killed tasks on other nodes. If spot preemption is too high, switch to on-demand nodes for critical jobs. If GC pauses are the root cause, address the underlying memory pressure by tuning memory limits or optimizing the code (see Executor OOM resolution). Identifying GC pauses requires looking at the Spark UI's Executors tab and checking the "GC Time" column.

Test Your Knowledge

A data engineer submits a PySpark job that reads a 500 GB dataset into a DataFrame and immediately calls df.toPandas(). The job fails within minutes. What is the most likely cause of this failure?

A
B
C
D
Test Your Knowledge

When analyzing a stack trace for a failed Databricks notebook, you see a Py4JJavaError. What is the correct approach to identifying the root cause of this error?

A
B
C
D
Test Your Knowledge

Which of the following scenarios is the most common cause of a Task not serializable (NotSerializableException) in Apache Spark?

A
B
C
D
Test Your Knowledge

Two separate Databricks Jobs run simultaneously. Both jobs attempt to execute a MERGE INTO operation on the exact same Delta Lake table. One job succeeds, but the other fails. Which exception is most likely thrown for the failed job?

A
B
C
D