Amazon Ads API 6 min read

Amazon Marketing Stream: “Value null at destination.firehoseDestination.subscriberRoleArn”

Your Marketing Stream subscription call returns a 400 complaining about a null subscriberRoleArn, and every code sample you can find sends only two ARNs. The subscribe body now needs three. Here's the current shape, the two other things that break right after you fix it, and why your first day of data looks empty.

•
Updated Oct 2026
Amazon Marketing Stream: “Value null at destination.firehoseDestination.subscriberRoleArn”

The 400 That Contradicts the Samples #

Update, October 2026: this error belongs to Marketing Stream V1, the Firehose/SQS version. Amazon has deprecated V1, and it stops delivering data on 30 June 2027. Stream V2, generally available since 30 September 2026, subscribes through a different endpoint with no role ARNs in the body and delivers Parquet to an S3 bucket you own. See what changes in Stream V2.

You're subscribing a dataset to an Amazon Marketing Stream destination. You send the body every reference implementation shows, and Amazon rejects it:

HTTP 400
Value null at 'destination.firehoseDestination.subscriberRoleArn'

The confusing part is that your body doesn't contain subscriberRoleArn at all, so "null" reads like Amazon is complaining about a field you never claimed to send. It is. The subscribe body now requires three ARNs, and older client code sends two.

If you built from an existing Marketing Stream integration, or from a sample written against the earlier API shape, this is where you land. The fix is one line — but the field naming is genuinely confusable, so it's worth being precise about what each ARN is.

A technician working on a server rack in a data centre
Marketing Stream pushes into your AWS — and delivers nothing until all three ARNs are right.

The Three ARNs, and Which Is Which #

Two of the three differ by a single word, which is most of why this trips people:

Field What it points at
deliveryStreamArnYour Kinesis Data Firehose delivery stream — where the records land
subscriptionRoleArnThe role Amazon assumes to write into your Firehose
subscriberRoleArnThe role representing you as the subscriber — the newly required one
body = {
    "dataSetId": data_set_id,
    "notes": "…",
    "destinationArn": firehose_arn,
    "destination": {
        "firehoseDestination": {
            "deliveryStreamArn":   delivery_stream_arn,
            "subscriptionRoleArn": subscription_role_arn,
            "subscriberRoleArn":   subscriber_role_arn,   # required — omitting it 400s
        }
    },
}

Read the error message literally when this happens: the path it gives you (destination.firehoseDestination.subscriberRoleArn) is exactly where the missing key belongs. Amazon's own Marketing Stream documentation is the authority on the current shape — check it rather than an older integration, because that's the whole cause of this error.

While you're in the IAM console: the trust policy on the role Amazon assumes points at a fixed Amazon-owned AWS account ID. It's a constant published in the docs, not something you generate — don't substitute your own account number into it.

Next: Your Firehose Objects Are Probably Gzipped #

You fix the ARN, the subscription goes ACTIVE, records start landing in S3 — and your parser throws on the first object.

If compression is enabled on your Firehose delivery stream, objects arrive GZIP-compressed, with .gz keys. Reference parsers written against an uncompressed stream read plain JSON and fall over. Since whether compression is on depends on how your delivery stream was configured, this is inconsistent between two otherwise identical integrations.

Sniff rather than trust the file extension — S3 keys aren't always what you expect:

import gzip

def read_object(body: bytes) -> bytes:
    # gzip magic number
    if body[:2] == b"\x1f\x8b":
        return gzip.decompress(body)
    return body

Two lines, and it works whether or not compression is enabled — which also means it keeps working if someone changes that setting later.

Then: Your First Day Looks Empty #

The last surprise isn't a bug at all, but it generates a lot of "is this working?" energy on day one.

Marketing Stream V1 is push-based and has no backfill. Data accrues from the moment your subscription becomes ACTIVE — there is no historical window, no request-past-data call, nothing to replay. Enrol on a Tuesday and Monday does not exist for you.

Update, October 2026: that is a V1 limit. A new Stream V2 subscription delivers the dataset's restatement period — 70 days for ads-performance-v1 — within about a day of activation, according to Amazon's V2 data delivery guide. Amazon also says that history can arrive at a coarser grain, such as daily, than the hourly data that follows.

Marketing Stream V1 data begins only at enrollment; history before that date must come from the reporting API subscription ACTIVE V1: no stream data — ever hourly records accrue backfill this from the Ads reporting API stream is the source of truth from here

So on V1, if you need history, pull it from the Ads reporting API and stitch it to the stream at the enrolment boundary. The reporting API can give you daily numbers for that period, but not months of hourly detail: even Amazon's new reporting API v1 keeps hour-level data for only 14 days.

Update, October 2026: Stream V2 changes the advice. Its 70-day backfill means a late start no longer means starting empty, but two limits remain: Amazon's V2 FAQ says the history "may arrive at a coarser grain (for example, daily) even when current deliveries for that dataset are hourly", and nothing older than 70 days is delivered at all. If you need long hourly history, subscribing early still pays — on V2, since V1 stops delivering on 30 June 2027.

Setting this up from scratch? The full Firehose wiring is in the Marketing Stream setup guide, the cost model is in what AMS actually costs in AWS, and AMS vs AMC covers which one you actually want.

A Debugging Order That Saves Time #

When a Marketing Stream subscription isn't producing data, check in this order — it's roughly the order of likelihood, and each step is cheap:

  1. Is the subscription ACTIVE? Not created — active. A subscription can be created and then fail to activate because of the trust policy.
  2. Does the trust policy on the subscription role name Amazon's account correctly? This is the most common activation failure.
  3. Is Firehose actually delivering? Check the delivery stream's own CloudWatch metrics before suspecting Amazon. Delivery failures to S3 are visible there and have nothing to do with the Ads API.
  4. Is your parser silently skipping objects? The gzip case fails loudly, but a broad except around object parsing turns it into "no data" — which looks like a subscription problem and isn't.
  5. Has enough time passed? Hourly datasets deliver hourly. An hour of nothing after enrolment is normal.

FAQ #

What causes “Value null at destination.firehoseDestination.subscriberRoleArn”?

Your subscribe request is missing the subscriberRoleArn field. The Marketing Stream subscribe body requires three ARNs — deliveryStreamArn, subscriptionRoleArn and subscriberRoleArn — and older client code and samples send only the first two.

What is the difference between subscriptionRoleArn and subscriberRoleArn?

subscriptionRoleArn is the IAM role Amazon assumes in order to write records into your Firehose delivery stream. subscriberRoleArn identifies you as the subscriber. They are separate fields and both are required.

Why does my Marketing Stream parser fail on the S3 objects?

If compression is enabled on your Firehose delivery stream, the objects are GZIP-compressed. Detect the gzip magic bytes 0x1f8b at the start of the object and decompress rather than assuming plain JSON.

Can I backfill historical Amazon Marketing Stream data?

On V1, no: it delivers data only from the point your subscription becomes active, so anything earlier has to come from the Amazon Ads reporting API. On Stream V2, generally available since 30 September 2026, yes: a new subscription delivers the dataset's restatement period, 70 days for ads-performance-v1, within about a day, although Amazon says that history can arrive at a coarser grain than hourly.

How long after subscribing should data appear?

For hourly datasets, expect the first records within about an hour of the subscription becoming ACTIVE. If nothing arrives after that, verify the subscription status and the delivery stream's CloudWatch metrics before assuming the subscribe call was wrong.

Running PPC across several accounts?

I built a dayparting control room that runs hourly across a PPC manager's client accounts: rules, dry runs, approvals and an audit log. Tell me what your tooling can't do and I'll quote the build at a fixed price.

Found this helpful? Share it: