Intermediate10 min read

DynamoDB Throttling and How to Fix It

Throttling is DynamoDB telling you a limit was hit. There are four different limits, three exceptions that report them, and the fix for one cause can make another worse.

Raising table capacity does nothing for a hot key, and switching to on-demand doesn't help one either; on-demand also throttles on its own rules.

Why is DynamoDB throttling my requests?

One of four documented limits was hit, and the exception's ThrottlingReasons field names which one. Read it before you resize anything:

  • A hot key: one partition went past 3,000 read units or 1,000 write units per second. This happens in both capacity modes.
  • Provisioned capacity: a provisioned table or GSI used more than its RCU or WCU.
  • The per-table quota: an on-demand table or GSI went past 40,000 read or write request units per second (the default).
  • Your own maximum: an on-demand table or GSI went past the maximum throughput you configured on it.

An on-demand table can also throttle while it scales past double its previous peak within 30 minutes.

The four throttling scenarios

AWS's own troubleshooting page splits throttling into exactly four cases:

  1. Key-range (partition) throughput exceeded, both modes. Every partition is designed for a maximum of 3,000 read units and 1,000 write units per second (partition-key docs), and item size counts against it. No table-level setting raises this; only key design spreads it. This is the hot partition case, and the table can look badly under-utilized while it throttles.
  2. Provisioned throughput exceeded, provisioned mode. Consumption beat the table's (or a GSI's) provisioned RCU/WCU, and the ~5-minute burst-capacity cushion was spent. The fix ladder is capacity-side: auto scaling, a higher provision, or a mode switch.
  3. Per-table quota exceeded, on-demand mode. An on-demand table or GSI went past its per-table quota, by default 40,000 read and 40,000 write request units per second per Region (quotas). It's adjustable: request more in the Service Quotas console. (The 80,000 per-account quota covers provisioned tables only and caps what you provision.)
  4. On-demand maximum throughput exceeded, on-demand mode. You set a maximum on the table or a GSI for cost control, and traffic went past it. Raise it, or set it to -1 to remove the cap (diagnosing throttling):
aws dynamodb update-table --table-name my-table \
  --on-demand-throughput MaxReadRequestUnits=20000,MaxWriteRequestUnits=8000

Apart from those four, an on-demand table ramps. A new one sustains 4,000 writes/s and 12,000 reads/s, then instantly takes up to double its previous peak.

Grow past double within 30 minutes and it can throttle (on-demand docs). Space the growth over at least 30 minutes, or pre-warm before a planned spike:

aws dynamodb update-table --table-name my-table \
  --warm-throughput ReadUnitsPerSecond=24000,WriteUnitsPerSecond=8000

Pre-warming is asynchronous (wait for WarmThroughput.Status to read ACTIVE), is billed, and can't be lowered afterward (warm throughput). On-demand throughput measures how a fresh table actually ramps.

The visualizer below plays out scenario 1. Skew the traffic toward one key and a single partition breaches its per-partition ceilings while the table's aggregate rate stays modest:

Partition load and throttling
8,000 RCU/s
2,500 WCU/s
45 %

The skewed share of all traffic lands on the single hot key USER#1000, which hashes to partition #2; the rest spreads evenly over 24 keys.

Hottest partition: #2 with 51.9% of the traffic — 1 of 8 partitions over a per-partition ceiling.

PartitionKeysTrafficReads (RCU/s)Writes (WCU/s)Status
#049.2%733229OK
#136.9%550172OK
#2351.9%4,1501,297Reads & writes throttled
#349.2%733229OK
#424.6%367115OK
#536.9%550172OK
#636.9%550172OK
#724.6%367115OK

Per-partition ceilings: 3,000 read units/s and 1,000 write units/s — a single physical partition never serves more, regardless of what the table has provisioned.

This is a simplified teaching hash, not DynamoDB’s real internal hash. DynamoDB uses an undocumented internal function and a partition count that grows with your table — use this only to build intuition for how distinct keys spread and a single hot key piles up.

The three exceptions, and the field that names the cause

  • ProvisionedThroughputExceededException: provisioned-mode throttling, "you exceeded your maximum allowed provisioned throughput for a table or for one or more global secondary indexes". A hot key on a provisioned table reports here too, with a KeyRangeThroughputExceeded reason.
  • ThrottlingException: control-plane operations issued too fast and, on on-demand tables, data-plane requests over a limit, such as the maximum you configured.
  • RequestLimitExceeded: account-level throughput limits. Request a higher quota in the Service Quotas console.

All three are marked retryable, and all three carry a list of ThrottlingReasons. Each entry has a reason of the form resource + operation + limit (TableReadProvisionedThroughputExceeded, IndexWriteKeyRangeThroughputExceeded, TableWriteAccountLimitExceeded) and a resource ARN naming the table or index (error reference).

In the JavaScript SDK v3 the field's casing differs by exception: ThrottlingException spells it throttlingReasons, the other two ThrottlingReasons. Read both:

const reasons = err.ThrottlingReasons ?? err.throttlingReasons ?? [];
for (const {reason, resource} of reasons) console.warn(reason, resource);

Key your handling on the reason string, not the exception class. AWS pages differ on whether account-limit throttling surfaces as RequestLimitExceeded or a ThrottlingException with an AccountLimitExceeded reason.

A throttled action inside TransactWriteItems can also surface as a canceled transaction: TransactionCanceledException with the cancellation code ThrottlingError or ProvisionedThroughputExceeded. SDKs don't retry that exception, so retry it yourself with backoff (transactions).

What absorbs load before you're throttled

Three built-ins soften limits, and knowing their edges explains "it worked yesterday":

  • Burst capacity retains up to five minutes (300 seconds) of unused read and write capacity for spikes. DynamoDB may also consume it for background maintenance "without prior notice", and AWS notes the details may change. Don't design for burst; treat it as luck.
  • Adaptive capacity automatically and instantly shifts throughput toward hot partitions and can isolate a frequently-accessed item onto its own partition, but only "provided that traffic does not exceed your table's total provisioned capacity or the partition maximum capacity". It rebalances skew; it never lifts the 3,000/1,000 per-partition ceiling, and it won't split item collections when the table has an LSI.
  • Split-for-heat, which the current AWS troubleshooting pages lean on, splits a partition under sustained heat. It takes time and does not help a single hot key.

Diagnose it from the metrics

CloudWatch separates requests from events, and the distinction does the diagnosis (metrics reference):

  • ThrottledRequests counts a request once if any event inside it was throttled: a PutItem on a table with three GSIs is one request but four write events. In a batch, it increments only if every item throttled.
  • ReadThrottleEvents / WriteThrottleEvents count each throttled event, so a BatchGetItem of 10 items is 10 GetItem events. To see a GSI's write throttles you must query the metric with both TableName and GlobalSecondaryIndexName, which is how GSI back-pressure hides from table-level dashboards.
  • The newer reason-specific event metrics (WriteProvisionedThroughputThrottleEvents, ReadKeyRangeThroughputThrottleEvents, …AccountLimitThrottleEvents, …MaxOnDemandThroughputThrottleEvents) split the counts by the same four causes. If your Region shows them, they answer the "which limit" question directly.

For a KeyRangeThroughputExceeded reason, find the key. Turn on CloudWatch Contributor Insights for the table or GSI in the resource ARN, in throttled-keys mode, and it lists the most-throttled keys (diagnosing throttling):

aws dynamodb update-contributor-insights --table-name my-table \
  --contributor-insights-action ENABLE --contributor-insights-mode THROTTLED_KEYS

If no single key stands out, the cause is usually sequential access across one key range: a Scan, or writes made in scan order. Rate-limit it below 3,000 read or 1,000 write units per second, or spread the reads with a parallel scan.

SDKs retry throttled requests automatically, so light throttling shows up as latency, not errors; watch the throttle metrics, not just your exception logs. The attempt count depends on your SDK: JavaScript v3's standard mode makes 3 by default.

Under the updated 2026 retry behavior (opt in with AWS_NEW_RETRIES_2026=true until it becomes the default), DynamoDB clients make 4 attempts. A throttling retry waits a random 0 to 1,000 ms, then up to 2,000 ms, then up to 4,000 ms (retry behavior).

KeyRangeyesnoProvisionedThroughputyesnoAccountLimitMaxOnDemandThroughputon-demand, otherThrottling observedLimit type inThrottlingReasons?Contributor Insights:one key stands out?Hot key:shard itSequential range:rate-limit or parallel scanresource ARNis an index?GSI back-pressure:scale the indexRaise capacity /auto scaling / switch modePer-table quota:Service Quotas increaseYour configured max:raise it or set -1Grew past 2x previous peak:pre-warm or spread the ramp

GSI back-pressure: the throttle that points at the wrong table

If any GSI can't absorb the write amplification, "DynamoDB throttles writes to the base table to maintain data consistency" (GSI throttling docs), even when the base table has capacity to spare. The exception's ResourceArn points at the index, but the operation that failed is your base-table write. Every index needs its own capacity plan (and its own auto scaling policy); why a GSI throttles base-table writes walks through the mechanics.

Match the fix to the cause

CauseWhat fixes itWhat doesn't
Hot key / partitionFind the key with Contributor Insights, then shard it (hot partitions); split-for-heat needs timeRaising table capacity, switching to on-demand
Provisioned capacityAuto scaling, higher min, or on-demandRetries alone (they add load)
GSI back-pressureScale the index; sparse-index or projection changesScaling the base table
Per-table quota (on-demand)Service Quotas increaseTable-level settings
Your on-demand maximumRaise MaxReadRequestUnits / MaxWriteRequestUnits, or set -1Waiting, or pre-warming
On-demand ramp past 2× peakPre-warm (warm throughput), or spread the growth over 30+ minutesRaising a maximum you never set

Do it in DynoTable

A filtered Scan consumes the full read whatever the filter keeps, which is how one dashboard query can throttle a busy table. In the SQL Workbench and PartiQL editors, DynoTable's pre-run cost preview identifies Scan versus Query and the index when a plan is available.

The RCU estimate appears only when table metadata supports it; an ordinary partition Query shows no RCU. The Scan vs Query guide covers the difference; the free item size calculator turns a real item into the RCU/WCU numbers the limits above are measured in.

Pitfalls and next steps

  • Retries amplify overload. Don't wrap SDK calls in your own retry loop; it multiplies pressure on the partition that's already struggling. Tune the SDK's max attempts instead, or, for a client that drives one table, switch to adaptive retry mode (AWS_RETRY_MODE=adaptive), which slows sends after throttles.
  • Batches hide partial throttling. A BatchWriteItem returns unprocessed items rather than throwing while any item succeeds. Resubmit UnprocessedItems (UnprocessedKeys for BatchGetItem) with exponential backoff, or use a helper that does it, such as boto3's batch_writer.
  • Table-level metrics miss GSI throttles. Chart throttle events per index; base-table dashboards look clean during back-pressure.
  • Capacity fixes don't move a hot key. Auto scaling and quota increases don't act instantly, and a hot key throttles in every capacity mode anyway. Shard it with a random or calculated suffix and read the shards back in parallel, as hot partitions shows, and see how partition keys work.

Download DynoTable to see whether a Workbench or PartiQL query will run as a Scan or a Query, and its estimated read cost where the plan allows, before it runs against your capacity.

By Dmitriy · Updated