# WooCommerce CSV Exports Are Silently Broken with S3 Uploads

If you use WooCommerce with S3 Uploads, your analytics exports might be quietly failing and not sending any emails, despite Action Scheduler saying the job was complete.

From **Analytics &gt; Revenue**, you click the **Download** button. The network tab shows the POST to `/wc-analytics/reports/revenue/export` with a payload:

```
{
  "report_args": {
    "interval": "day",
    "orderby": "date",
    "order": "desc",
    "page": 1,
    "per_page": 25,
    "after": "2025-01-01T00:00:00",
    "before": "2025-12-31T23:59:59"
  },
  "email": true
}

```

The response includes an `export_id` and a status link:

```
{
  "message": "Your report file is being generated.",
  "export_id": "12312312312312",
  "_links": {
    "status": [
      {
        "href": "https://example.com/wp-json/wc-analytics/reports/revenue/export/12312312312312/status"
      }
    ]
  }
}

```

But then no email is sent.

Checking **WooCommerce &gt; Status &gt; Scheduled Actions**, the `woocommerce_admin_report_export` batch jobs show "Complete." The `woocommerce_admin_email_report_download_link` action ran and everything looks healthy in the admin.

Since the email never arrived, we dug into the export status endpoint code and found that when `percent_complete` reaches 100, it constructs a `download_url` from the export ID and report type. Using the export ID from the Scheduled Actions args, we manually hit:

```
/wp-admin/?action=woocommerce_admin_download_report_csv&filename=wc-revenue-report-export-12312312312312
```

A CSV downloaded, but with only headers:

```
Date,Orders,"Gross sales",Returns,Coupons,"Net sales",Taxes,Shipping,"Total sales"
```

## Why it's silent

WooCommerce's batch exporter writes CSV data in [`WC_CSV_Batch_Exporter::write_csv_data()`](https://github.com/woocommerce/woocommerce/blob/bf1129d9178f5d4b48b64217ef2697b97e218296/plugins/woocommerce/includes/export/abstract-wc-csv-batch-exporter.php). The file operations use `@fopen` and `@file_put_contents` with silenced errors. When the write fails, no exception is thrown, nothing is logged, and the Action Scheduler job just "completes" with zero data written.

## What's happening

`write_csv_data()` opens the CSV file with fopen mode `a+` (read + append):

```
$fopen_mode = apply_filters( 'woocommerce_csv_exporter_fopen_mode', 'a+' );
$fp = fopen( $this->get_file_path(), $fopen_mode );

```

When you're running [S3 Uploads](https://github.com/humanmade/S3-Uploads), `wp_upload_dir()` returns an `s3://` path. The S3 stream wrapper only supports `r`, `w`, `a`, and `x` — the `+` variants require seekable streams, which S3 objects aren't. So `fopen` fails, `$fp` is false, and the data is never written.

When we tried generating the export via WP-CLI, the errors were no longer silenced:

```
PHP Warning: Mode not supported: a+. Use one 'r', 'w', 'a', or 'x'.
  in /var/www/public/app/mu-plugins/s3-uploads/inc/class-stream-wrapper.php on line 985

PHP Warning: fopen(s3://bucket/uploads/woocommerce_uploads/reports/wc-revenue-report-export.csv):
  Failed to open stream: "S3_Uploads\Stream_Wrapper::stream_open" call failed
  in /var/www/public/app/plugins/woocommerce/includes/export/abstract-wc-csv-batch-exporter.php on line 151
```

## Fixing the CSV generation

WooCommerce added a hook in 6.8.0 via [woocommerce/woocommerce#33652](https://github.com/woocommerce/woocommerce/pull/33652) that defaults to `a+`.

To fix the CSV generation, you'll have to add a filter that overrides this to `a`, which is S3-compatible:

```
add_filter('woocommerce_csv_exporter_fopen_mode', fn () => 'a');

```

If you're routing uploads through any S3 stream wrapper, you're affected by this. The exports will always produce headers-only files with no data rows and the email is never sent.

Issue submitted to WooCommerce: <https://github.com/woocommerce/woocommerce/issues/63835>