← Back to list

The MWAA Plugin Puzzle That Cost Me Hours (And How You Can Avoid It)

A tale of zip files, directory structures, and the tiny details that break production deployments

Sanjeeb Panda · 2026-01-08 17:03 · 21 claps · 2.7 min read
#airflow #data-engineering #aws-mwaa #aws
Open on Medium ↗
Wiki topics: ☁️ · DevOps & Cloud 🔧 · Data Engineering 🎬 · Film & Television

The MWAA Plugin Puzzle That Cost Me Hours (And How You Can Avoid It)

A tale of zip files, directory structures, and the tiny details that break production deployments

Ever spent hours debugging something, only to discover the fix was embarrassingly simple? Welcome to my Tuesday.

I was setting up custom sensors for Amazon Managed Workflows for Apache Airflow (MWAA), and everything looked perfect. The code was tested. The DAGs were ready. The plugin zip file was uploaded to S3. But Airflow kept throwing import errors like it had never heard of my beautiful custom sensor.

Spoiler alert: It was the zip file structure. Of course it was.

The Setup

I had built a custom S3 date range sensor — nothing fancy, just a sensor that checks for data across a date range in S3. The kind of thing you’ve probably written a dozen times.

The code worked locally. It worked in my dev environment. It passed all tests. So I zipped it up and deployed it to MWAA.

And then… nothing. Import errors. Module not found. The classic.

The Investigation

After checking the obvious things (S3 paths, IAM permissions, MWAA environment updates), I finally did what I should have done first — I actually looked at what was inside my zip file.

I had two versions sitting in my downloads folder:

  • One from my first attempt
  • One from my “fixed” attempt

Same code inside. Same file sizes for the actual Python files. But one worked, and one didn’t.

The difference?

Version 1 (broken):

my_project/plugins/
├── __init__.py
└── sensors/
    ├── __init__.py
    └── s3_date_range_sensor.py

Version 2 (working):

sensors/
├── __init__.py
└── s3_date_range_sensor.py
__init__.py

That’s it. That’s the whole difference. The nested directory path.

Why This Matters

MWAA expects your plugin structure to start immediately at the zip root. When Airflow unpacks your plugins.zip, it looks for Python modules right there at the top level.

So when you write this in your DAG:

from sensors.s3_date_range_sensor import S3DateRangeSensor

Airflow is looking for a sensor

folder at the root of the extracted plugins. If your zip file has

my_project/plugins/sensors/...

, Airflow can’t find it. It’s not going to traverse your nested directory structure hoping to stumble upon your code.

The Fix

When creating your plugins.zip, make sure you’re zipping from the right directory.

Wrong approach:

cd ~/projects
zip -r plugins.zip my_project/plugins/

Right approach:

cd ~/projects/my_project/plugins
zip -r plugins.zip .

Or if you want to be more explicit:

cd ~/projects/my_project/plugins
zip -r ../../../plugins.zip sensors/ __init__.py

A Quick Sanity Check

Before uploading, always verify your zip structure:

unzip -l plugins.zip

You should see something like:

Archive:  plugins.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  01-07-2025 18:07   sensors/
      156  01-07-2025 18:07   sensors/__init__.py
    23525  01-07-2025 18:07   sensors/s3_date_range_sensor.py
       89  01-07-2025 18:07   __init__.py

Not this:

Archive:  plugins.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  01-08-2025 11:46   my_project/plugins/sensors/
      ...

Lessons Learned

  1. Always inspect your zip files before deploying. A quick
  • unzip -ltakes two seconds and saves hours of debugging.

2. Zip from the right directory. The directory you run from matters enormously.

3. MWAA is particular. It does what the documentation says, even when you think you know better.

4. Keep your failed attempts around. Having both zip files let me actually compare and find the issue. If I’d deleted the broken one, I’d probably still be debugging.

The Broader Point

This wasn’t a code problem. The Python was identical in both zip files — byte for byte the same. This was purely a packaging problem.

In the world of managed services like MWAA, understanding how to package your code is just as important as the code itself. The service has expectations about structure, and it’s not going to figure out what you meant. You have to meet it where it is.

Have you ever spent way too long on a packaging issue? I’d love to hear about it in the comments — misery loves company.

Tags: #AWS #MWAA #Airflow #Python #DevOps #DataEngineering


메타데이터
post_id
2bef2295438b
slug
the-mwaa-plugin-puzzle-that-cost-me-hours-and-how-you-can-avoid-it-2bef2295438b
url
https://medium.com/@sanjeebmeister/the-mwaa-plugin-puzzle-that-cost-me-hours-and-how-you-can-avoid-it-2bef2295438b
canonical_url
https://medium.com/@sanjeebmeister/the-mwaa-plugin-puzzle-that-cost-me-hours-and-how-you-can-avoid-it-2bef2295438b
author_url
https://medium.com/@sanjeebmeister
status
ok
fetched_at
2026-07-07 08:01:35