DynamoDB BatchWriteItem with the AWS CLI

aws dynamodb batch-write-item puts or deletes up to 25 items in one command. From the shell it has two sharp edges the SDKs soften: every value is DynamoDB JSON you have to quote correctly, and the CLI has no mechanism at all for draining UnprocessedItems. The limits and the partial-failure model are in batch operations in DynamoDB.

Code

aws dynamodb batch-write-item \
  --request-items '{
    "Music": [
      {"PutRequest": {"Item": {"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "Cubano Chant"}, "AlbumTitle": {"S": "Danzon"}, "Year": {"N": "1994"}}}},
      {"PutRequest": {"Item": {"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "A Mis Abuelos"}, "AlbumTitle": {"S": "Danzon"}, "Year": {"N": "1994"}}}},
      {"DeleteRequest": {"Key": {"Artist": {"S": "Ella Fitzgerald"}, "SongTitle": {"S": "Misty"}}}}
    ]
  }'

Run against DynamoDB Local 3.3.0 that prints, in full:

{
    "UnprocessedItems": {}
}

Explanation

  • An empty leftover map is the only success signal you get. The command prints UnprocessedItems and nothing else, so a script that checks only the exit status will call a half-written batch a success. Parse the map; jq -e '.UnprocessedItems | length == 0' is the whole check.
  • No flag to drain itaws dynamodb query help offers --starting-token, --max-items and --page-size. aws dynamodb batch-write-item help offers none of them, because UnprocessedItems is not a pagination cursor. Re-feeding it is a shell loop with a sleep, and it is already in --request-items shape.
  • --condition-expression and --return-values are not accepted here, and that is the API, not the CLI: conditions cannot be attached to individual put and delete requests. Every PutRequest replaces the whole stored item, so a batch built from a partial payload deletes the attributes it left out.
  • Use file://, not inline JSON. --request-items file://writes.json removes shell quoting from the list of things that can be wrong, which matters because most of what goes wrong in this command is quoting.
  • One bad entry, all 25 lost — a missing table, a key that does not match the schema, a >400 KB item, >16 MB total, a partition key over 2048 bytes or a sort key over 1024 bytes each reject the entire batch rather than the offending entry.

What the command prints, including the rejections

Add --return-consumed-capacity TOTAL to the fence above and DynamoDB Local 3.3.0 answers:

{
    "UnprocessedItems": {},
    "ConsumedCapacity": [
        {
            "TableName": "Music",
            "CapacityUnits": 3.0
        }
    ]
}

Three units for two puts and one delete: the batch bought one round trip, not a discount. Each entry is billed as the individual PutItem or DeleteItem it stands for, rounded up to 1 KB.

Run the delete a second time, when Ella Fitzgerald / Misty is already gone, and DynamoDB Local reports 2.0 units for that single DeleteRequest. The BatchWriteItem reference (fetched 2026-07-28) says a delete on a nonexistent item consumes one write capacity unit, and a standalone delete-item against the same local engine does report 1.0. Treat local capacity numbers as directional. The point that survives either way is that a delete which finds nothing is still billed.

Two requests the service refuses outright, on stderr, exit status 254:

aws: [ERROR]: An error occurred (ValidationException) when calling the BatchWriteItem operation: Too many items requested for the BatchWriteItem call
aws: [ERROR]: An error occurred (ValidationException) when calling the BatchWriteItem operation: Provided list of item keys contains duplicates

The second one is worth staring at. It was produced by a PutRequest and a DeleteRequest on the same key, not by two puts. DynamoDB counts any second operation on one item in one batch as a duplicate, so "delete the old row and write the new one" fails as a single batch even though the two entries look nothing alike.

Assembling those value maps inside single quotes is where the time goes. The DynamoDB Expression Builder produces typed maps and copies out a runnable command, so a failure is at least a real one rather than a stray backslash.

To bulk-load or clear items from CSV or JSON without escaping any of it, download DynoTable.

References

Last verified 2026-07-28 against the official AWS documentation linked above.

Work with DynamoDB without the Console

A fast DynamoDB desktop client that runs the real SQL DynamoDB can’t — JOINs, GROUP BY, aggregates — with visual editing and an AI agent on your own Bedrock keys.

Free 30-day trial, no credit card — then the Free plan with no time limit.