ProvisionedThroughputExceededException: The level of configured provisioned throughput for the table was exceeded

Short answer

DynamoDB rejected the read or write because the capacity available to it was already consumed. With provisioned capacity that means the table's configured units; with on-demand it usually means a hot partition — one key taking far more traffic than the rest.

What it means

DynamoDB allocates throughput per partition, not just per table, and that single fact explains most of the surprise this exception causes. A table's capacity — provisioned or on-demand — is distributed across its partitions, and each partition has a hard ceiling of its own. A table nowhere near its configured limit can therefore throttle continuously if the traffic is concentrated on one key.

That is the difference between the two shapes of this failure. Broadly distributed throttling means the table needs more capacity, and raising it works. Throttling concentrated on one partition key means the table has plenty of unused capacity that the hot key cannot reach, and raising the number changes nothing. The keys involved in the failures tell them apart, and it is worth checking before provisioning more.

Lambda makes both shapes arrive faster than they otherwise would, because concurrency multiplies request rate. A function issuing five writes per invocation is issuing five hundred concurrent writes at a hundred concurrent executions, and Lambda will happily go from one execution to hundreds within seconds when a queue backs up or a scheduled job fans out. The table sees a step change rather than a ramp, which is also why on-demand tables throttle during spikes — on-demand adapts to traffic, but it adapts over minutes, not instantly.

The quietest version of this problem is the one that never raises an exception at all. The AWS SDKs retry throttled requests with backoff, so moderate throttling is absorbed and shows up only as invocations that take noticeably longer than they used to. And BatchWriteItem does not throw for a partially-throttled batch — it returns the remainder in UnprocessedItems, which code that ignores the field silently discards. A rising p95 duration with no errors is often the first symptom, arriving well before anything appears in a log as a failure.

Depends

CloudWatch’s Errors metric: Whether CloudWatch’s Errors metric counts this depends on whether the error escaped your handler, which the log line alone does not settle. Counted only when the exception escapes the handler; the AWS SDKs retry these automatically, so many are absorbed and never fail an invocation at all. LogStitch reports this invocation's status as throttled rather than errored, which is the same distinction CloudWatch draws between its Throttles and Errors metrics.

What it looks like in CloudWatch

This is the shape the failure arrives in: the lines of one invocation scattered among everything else the log group received at the same moment.

CloudWatch Logs Before

7 raw lines, in the order CloudWatch delivered them.

2026-08-29T12:44:18.114Z START RequestId: 4f81a26d-3b90-4c57-8e02-9d5c7a1f3b28 Version: $LATEST
2026-08-29T12:44:18.118Z 4f81a26d-3b90-4c57-8e02-9d5c7a1f3b28 INFO Ingest batch size=25 table=orders
2026-08-29T12:44:18.640Z 4f81a26d-3b90-4c57-8e02-9d5c7a1f3b28 WARN Unprocessed items returned count=19 attempt=1
2026-08-29T12:44:19.402Z 4f81a26d-3b90-4c57-8e02-9d5c7a1f3b28 WARN Unprocessed items returned count=19 attempt=2
2026-08-29T12:44:20.118Z 4f81a26d-3b90-4c57-8e02-9d5c7a1f3b28 ERROR ProvisionedThroughputExceededException: The level of configured provisioned throughput for the table was exceeded. Consider increasing your provisioning level with the UpdateTable API.
2026-08-29T12:44:20.402Z END RequestId: 4f81a26d-3b90-4c57-8e02-9d5c7a1f3b28
2026-08-29T12:44:20.402Z REPORT RequestId: 4f81a26d-3b90-4c57-8e02-9d5c7a1f3b28 Duration: 2288.11 ms Billed Duration: 2289 ms Memory Size: 512 MB Max Memory Used: 122 MB Status: error

LogStitch After

The same lines, grouped into the invocation they belong to.

Throttled4f81a26d-3b90-4c57-8e02-9d5c7a1f3b28dur 2.29sbilled 2.29smem 122/512MBlogs 7
  1. 12:44:18.114PLAT
    START RequestId: 4f81a26d-3b90-4c57-8e02-9d5c7a1f3b28 Version: $LATEST
  2. 12:44:18.118INFO
    INFO	Ingest batch size=25 table=orders
  3. 12:44:18.640WARN
    WARN	Unprocessed items returned count=19 attempt=1
  4. 12:44:19.402WARN
    WARN	Unprocessed items returned count=19 attempt=2
  5. 12:44:20.118ERROR
    ERROR	ProvisionedThroughputExceededException: The level of configured provisioned throughput for the table was exceeded. Consider increasing your provisioning level with the UpdateTable API.
  6. 12:44:20.402PLAT
    END RequestId: 4f81a26d-3b90-4c57-8e02-9d5c7a1f3b28
  7. 12:44:20.402PLAT
    REPORT RequestId: 4f81a26d-3b90-4c57-8e02-9d5c7a1f3b28	Duration: 2288.11 ms	Billed Duration: 2289 ms	Memory Size: 512 MB	Max Memory Used: 122 MB	Status: error
Throttled
Status
2.29s
Duration
2.29s
Billed
122/512MB
Memory
76%
Headroom
No
Cold start
Throttled
Error type

The panel on the right is generated by running the excerpt on the left through the same parser that powers the free web stitcher — it is what the tool actually produces for this input, not an illustration of it.

How to confirm it from the logs

First establish whether the exception escaped or was absorbed. The SDKs retry throttled DynamoDB requests with backoff, so a logged exception generally means the retries were exhausted. A REPORT duration much longer than usual, with no error at all, is the same throttling absorbed successfully — and is the earlier warning.

Then look at whether the affected keys are the same across failures. Throttling spread evenly across many keys points at overall capacity; throttling concentrated on one partition key points at a hot partition, which more capacity will not fix because a single partition has its own ceiling regardless of the table's total.

Causes, most likely first

1

Provisioned capacity is below the actual demand

How to confirm

Compare the table's consumed capacity against its provisioned capacity over the period. If consumption sits at the ceiling during the failures, the table is simply under-provisioned for the traffic.

2

A single partition key is taking a disproportionate share

How to confirm

Look at which keys appear in the throttled requests. A partition has a fixed maximum throughput of its own, so one very popular key — a tenant id, a current date, a status flag — can be throttled while the table as a whole is far below its provisioned total.

3

Lambda concurrency is multiplying the request rate

How to confirm

Check concurrent executions during the failures. A function reading and writing a handful of items per invocation generates a request rate equal to that multiplied by concurrency, and a queue drain or scheduled fan-out can raise concurrency by orders of magnitude in seconds.

4

On-demand capacity is still scaling up

How to confirm

Check whether the table is on-demand and whether the spike was sudden. On-demand adapts to traffic but not instantaneously — it accommodates roughly double the previous peak immediately and takes time to go beyond that, so a step change can be throttled.

5

An index is the bottleneck rather than the table

How to confirm

Read which resource the exception names. A global secondary index has its own capacity, and a write to the table that also writes to an under-provisioned index is throttled by the index — while the table's own metrics look entirely healthy.

Fixes

Fix 1

Move to on-demand, or raise provisioned capacity with auto-scaling

On-demand removes capacity planning for variable traffic and is usually the right default for a Lambda-backed table. Where provisioned capacity is cheaper for a steady workload, pair it with auto-scaling so it tracks demand instead of a guess.

yamlResources:
  OrdersTable:
    Type: AWS::DynamoDB::Table
    Properties:
      BillingMode: PAY_PER_REQUEST   # was PROVISIONED
Fix 2

Spread a hot partition key

When one key is the bottleneck, more capacity does not help — a single partition has its own ceiling. Add a suffix so the writes distribute, and query the shards in parallel on the way back out.

js// Hot: every event for a given day lands on one partition.
// const pk = `ORDERS#${day}`;

// Spread across N partitions, then fan the query out over the same N.
const SHARDS = 10;
const pk = `ORDERS#${day}#${Math.floor(Math.random() * SHARDS)}`;
Fix 3

Cap the function's concurrency so it cannot outrun the table

Reserved concurrency limits how many invocations can run at once, which directly limits the request rate the function can generate. This is the standard fix when a queue consumer scales faster than the table behind it.

yamlResources:
  IngestFunction:
    Type: AWS::Serverless::Function
    Properties:
      ReservedConcurrentExecutions: 20
      Events:
        Queue:
          Type: SQS
          Properties:
            BatchSize: 25
Fix 4

Handle unprocessed items rather than assuming success

BatchWriteItem and BatchGetItem do not throw when only part of the batch is throttled — they return the remainder in UnprocessedItems. Ignoring that field silently loses writes, which is worse than the throttle.

jslet request = { RequestItems: { orders: writes } };
for (let attempt = 0; attempt < 5; attempt += 1) {
  const result = await client.send(new BatchWriteItemCommand(request));
  const left = result.UnprocessedItems?.orders ?? [];
  if (left.length === 0) break;
  await sleep(2 ** attempt * 50);
  request = { RequestItems: { orders: left } };
}

Also seen as

The same underlying failure, worded differently by a different runtime, SDK version, or logging layer. All of these land here — there is no separate page for each phrasing.

The level of configured provisioned throughput for the table was exceeded. Consider increasing your provisioning level with the UpdateTable API.Throughput exceeds the current capacity of your table or indexdynamodb provisioned throughput exceeded lambdadynamodb hot partition

Errors that show up alongside this one, or that people mistake for it.

References

LogStitch finds this automatically, across every invocation in your account.

Paste a log excerpt into the free web stitcher and see it grouped, classified, and measured in your browser — nothing is uploaded. Or run the Mac app against your own AWS profiles and get the same view over every function you own.