DynamoDB TransactWriteItems with the AWS CLI

The whole transaction goes to aws dynamodb transact-write-items as one --transact-items JSON array, so the CLI's edges are the interesting part: where quoting breaks, what the exit code means, and the fact that the default error output drops the field you need to debug a cancellation. What a transaction buys you is the same in every SDK.

Code

aws dynamodb transact-write-items \
  --transact-items '[
    {
      "Update": {
        "TableName": "Music",
        "Key": {"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "Cubano Chant"}},
        "UpdateExpression": "SET #upd0 = #upd0 - :one",
        "ConditionExpression": "#upd0 >= :one",
        "ExpressionAttributeNames": {"#upd0": "Awards"},
        "ExpressionAttributeValues": {":one": {"N": "1"}}
      }
    },
    {
      "Update": {
        "TableName": "Music",
        "Key": {"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "A Mis Abuelos"}},
        "UpdateExpression": "SET #upd0 = if_not_exists(#upd0, :zero) + :one",
        "ExpressionAttributeNames": {"#upd0": "Awards"},
        "ExpressionAttributeValues": {":one": {"N": "1"}, ":zero": {"N": "0"}}
      }
    }
  ]'

A committed transaction prints nothing and exits 0. There is no response body to check, so in a script the exit code is the result.

Explanation

  • --transact-items — up to 100 Put / Update / Delete / ConditionCheck actions, 4 MB aggregate, values in DynamoDB JSON. Actions can span tables in the same account and Region, and no two of them may target the same item.

  • Three exit codes, three different failures. 0 committed. 252 means the CLI's own parameter validation rejected the request and nothing was sent. 254 means DynamoDB answered and said no. That distinction is worth branching on: a 252 is a bug in your JSON, a 254 may be a condition you expected to fail.

  • Default error format — drops the per-action reasons. aws-cli v2 prints the summary and then tells you it is withholding the detail:

    aws: [ERROR]: An error occurred (TransactionCanceledException) when calling the TransactWriteItems operation: Transaction cancelled, please refer cancellation reasons for specific reasons [ConditionalCheckFailed, None]
    
    Additional error details:
    CancellationReasons: <complex value>
    Use "--cli-error-format json" or another error format to see the full details.

    Re-run the same command with --cli-error-format json and the structure arrives intact, one entry per action, in --transact-items order:

    {
        "Message": "Transaction cancelled, please refer cancellation reasons for specific reasons [ConditionalCheckFailed, None]",
        "Code": "TransactionCanceledException",
        "CancellationReasons": [
            {
                "Code": "ConditionalCheckFailed",
                "Message": "The conditional request failed"
            },
            {
                "Code": "None"
            }
        ]
    }

    Here the first update's Awards >= 1 condition failed; None marks the second action as innocent, and note it carries no Message field at all. Every other code is decoded on the TransactionCanceledException page.

  • Targeting one item twice is not a cancellation. It fails validation before anything is attempted, which is why there are no reasons to print:

    aws: [ERROR]: An error occurred (ValidationException) when calling the TransactWriteItems operation: Transaction request cannot include multiple operations on one item
  • ConditionCheck — asserts a condition on an item the transaction does not modify, and vetoes the whole transaction if it fails.

  • --client-request-token — a fixed token makes re-runs idempotent for 10 minutes. Reuse the same token with any parameter changed and DynamoDB returns IdempotentParameterMismatch rather than silently applying the new payload.

  • The array in a file--transact-items file://transaction.json sidesteps shell quoting entirely, and the file is diffable.

The 2× is measurable from the shell

Run the same single-item update twice, once inside a transaction and once outside, both with --return-consumed-capacity TOTAL. DynamoDB Local reports 2.0 capacity units for the transactional write and 1.0 for the plain one: the prepare and the commit each bill.

That is the whole argument against reaching for a transaction by default. For atomicity on one item you already have a cheaper tool in a conditional write, which bills once. To price a workload that does this millions of times, the DynamoDB pricing calculator takes the doubled write count directly. If assembling DynamoDB JSON in a shell is the part you want to stop doing, DynoTable edits items against a real table and shows you the expression it generated.

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.