AWS Glue Silent Failures: A 3-Day Debugging Story Nobody Talks About
When Your ETL Job Dies Quietly and Your Monitoring Tools Say Nothing

Search for a command to run...
When Your ETL Job Dies Quietly and Your Monitoring Tools Say Nothing

No comments yet. Be the first to comment.
Side-by-Side Comparison From Someone Who's Used Both in Production

A Career Pivot Story from Nepal (And Why Your Degree Doesn't Matter as Much as You Think)

From Zero to Spark Hero — Without Spending a Dime

Let me tell you about the day I spent 72 hours debugging an AWS Glue job that wasn't actually broken.
The story started simple. We had a Glue ETL job running on schedule in production. Raw data from S3, transformation using PySpark, write back as Parquet. Downstream, Amazon Athena would query the output for business intelligence dashboards.
The job worked fine in dev. Ran perfectly for two weeks in production. Then one Tuesday afternoon it just... failed. No error message. No stack trace. Just "Job run failed" in the Glue console.
This is the problem with Glue. When it fails, it fails quietly.
Our first instinct was to check the Apache Airflow logs. We use MWAA (Managed Workflows for Apache Airflow) to orchestrate the Glue job.
[2024-01-15 14:32:15] Task failed with error:
ClientError: An error occurred (InvalidInputException) when calling the
RunJob operation: The supplied role ARN is invalid.
Except... the role ARN was the same one we've been using for two weeks. So why now?
Checked IAM permissions. Looked fine. Restarted the MWAA task. Same error. Wasted 4 hours here.
Opened the AWS Glue job run details page. This is where it gets frustrating. The console showed:
Job Status: FAILED
Error: Job run failed
Reason: See logs for details
Last Update: 2 minutes ago
That's it. That's the entire error message. "See logs for details." Okay, Glue. Let's go to the logs then.
Here's what Glue doesn't tell you upfront: It logs to THREE different CloudWatch log groups simultaneously, and the error is almost never in the obvious one.
The three log groups are:
/aws-glue/jobs/output — Driver logs (usually useless for actual errors)/aws-glue/jobs/error — This is where you want to be/aws-glue/jobs/glue-python-shell — Only for Python Shell jobs (we're using Spark, not relevant)I found the error logs buried under /aws-glue/jobs/error in an executor log stream (not the driver). The job had multiple workers, and each worker had its own log stream. The error was in worker 3 of 4.
Worker-3 - ERROR - Executor 5:
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3145)
at org.apache.spark.sql.catalyst.expressions.codegen.CodeGenerator.compile(CodeGenerator.scala:1069)
... [20 more stack traces]
Found it. Out of memory. A single worker ran out of RAM.
Dug deeper. The job was processing one day's worth of data. But one specific date partition had 10 gigabytes of data. All other partitions? 800 megabytes to 1.2 gigabytes.
That one huge partition was assigned to a single worker. Worker memory? Default 2 GB per node.
Why was the partition so big? A month ago, someone ran a manual backfill operation. They loaded historical data and didn't partition it correctly. It all went into one date bucket.
But wait, there was more. The job was using the default number of DPUs. For context, AWS Glue measures compute in "Data Processing Units" (DPUs). Default is 2 DPUs = 1 driver + 1 worker.
We were processing production data with the compute equivalent of a laptop from 2015.
While reading the Glue job configuration, I noticed job bookmarks were disabled. This meant every time the job ran, it reprocessed ALL data, not just new data.
So on that Tuesday:
This is the silent failure problem. The error happened at the executor level, not the driver level. The driver says "uh, I lost connection to an executor" and then gives up.
# Before: Default 2 DPU
# After: 5 DPU = 1 driver + 4 workers
cluster_config = {
"Name": "production-etl-glue-job",
"NumberOfWorkers": 4,
"WorkerType": "G.2X", # General purpose, 8GB RAM each
"GlueVersion": "4.0",
"MaxCapacity": None, # Using WorkerType, not MaxCapacity
"Timeout": 60, # 60 minutes, up from default 30
}
More workers = data distributed across more machines = no single worker drowns.
# In the Spark transformation
df = spark.read.parquet("s3://raw-data/")
# Transform...
transformed_df = df.filter(...).select(...).groupBy(...).agg(...)
# CRITICAL: Repartition by date before writing
# This ensures even distribution, not by accident
repartitioned_df = transformed_df.repartition(32, "date_column")
# Write with partitioning
repartitioned_df.write \
.mode("overwrite") \
.partitionBy("year", "month", "day") \
.parquet("s3://processed-data/")
The repartition() call redistributes data across workers. Even if your source data is uneven, you're redistributing it equally before the write.
# Glue job config
{
"JobBookmarkOption": "job-bookmark-enable",
"MaxRetries": 1,
"Timeout": 60,
# ... rest of config
}
Job bookmarks track which data has been processed. Next run only processes new data. Saves time and compute costs.
# In your Terraform or CloudFormation
resource "aws_cloudwatch_metric_alarm" "glue_job_failures" {
alarm_name = "glue-job-production-etl-failures"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "1"
metric_name = "glue_job_run_failure"
namespace = "AWS/Glue"
period = "300"
statistic = "Sum"
threshold = "1"
alarm_description = "Alert when Glue job fails"
alarm_actions = [aws_sns_topic.alerts.arn]
}
Instead of discovering failures from Athena dashboards going stale, you get paged immediately.
Driver logs are garbage. They don't see what workers are doing. The real error? Always in executor logs, always harder to find.
Lesson: First thing when Glue fails: go to CloudWatch, find /aws-glue/jobs/error log group, search for "ERROR" in executor log streams (not driver).
Default 2 DPU worked for toy datasets. Real production data laughs at defaults.
Lesson: Measure your data size. Do the math. 50 GB of data? You need at least 10-15 DPU. Let Glue estimate then add 40% overhead.
One huge partition and three normal ones? That one partition becomes your bottleneck. One worker drowns while three others are idle.
Lesson: Always inspect your partition sizes in S3. Look at the distribution. If something is 10x larger, investigate.
Reprocessing all data every run is wasteful. But more importantly, it makes failures more likely because you're processing way more data than necessary.
Lesson: Enable bookmarks before your first production run. Yes, it complicates debugging locally. It saves your skin in production.
We discovered the Glue failure from a Slack bot that checks Athena table freshness. That's a human detecting a machine failure. Backwards.
Lesson: Alarm on Glue job failures before anything else alarms on missing data.
Total: 3 days for a fix that was 20 minutes once we knew what to fix.
That's not a failure of Glue. That's a failure of observability. We had no way to see what was happening at the executor level until we manually opened CloudWatch logs and scrolled through thousands of lines.
Glue is solid. It's been battle-tested by thousands of companies. But it's designed for when things work. When they break, it's like debugging in the dark.
The error handling is... minimal. The monitoring is.. optional. The logging is... verbose but disorganized.
Here's what you need to know:
Glue does:
Glue doesn't:
You have to do that part.
If you're running Glue jobs in production, do this:
.repartition() before writes to ensure even distributionThe 72 hours I spent debugging would have been 30 minutes if I'd done these things upfront.
Don't learn this lesson the hard way. Learn it from someone who did.
Q: Will increasing DPU to 5 cost a fortune?
A: DPU costs ~\(0.44/DPU per hour on-demand. 5 DPU × 1 hour = ~\)2.20. Daily job. ~$66/month extra if run daily. Worth it to avoid 3-day debugging sessions.
Q: Can I use Spark repartition() without running out of memory?
A: Not if you're already out of memory. Repartition shuffles data across the network. If you're hitting memory limits, do repartition BEFORE the transformation that's causing memory issues, not after.
Q: Job bookmarks broke my historical reprocessing. What now?
A: Bookmarks have a reset option. You can disable it for a run, or reset the bookmark state. Use it during backfills, then re-enable.
Q: Should I use Databricks instead?
A: Different problem. Databricks is SQL + ML platform. Glue is ETL orchestration. Different products, different use cases. This article is about Glue in production and making it not fail silently.
Have you hit Glue failures before? What did your error message actually say? Drop it in the comments. Bet you anything it was unhelpful and you spent 12 hours finding the real issue.