DynamoDB PutItem with the AWS CLI

aws dynamodb put-item writes a whole item and replaces any existing item with the same primary key (item-based actions covers how that differs from update-item). The CLI's own contribution to the problem is the shell: --item takes DynamoDB JSON as a single quoted argument, and every attribute value is typed.

Code

aws dynamodb put-item \
  --table-name 'Music' \
  --item '{"Artist":{"S":"Arturo Sandoval"},"SongTitle":{"S":"Cubano Chant"},"AlbumTitle":{"S":"Danzon"},"Year":{"N":"1994"},"Awards":{"N":"0"}}' \
  --condition-expression 'attribute_not_exists(#cond0) AND attribute_not_exists(#cond1)' \
  --expression-attribute-names '{"#cond0":"Artist","#cond1":"SongTitle"}'

On success the command prints nothing and exits 0. If the item already exists, the condition fails:

An error occurred (ConditionalCheckFailedException) when calling the PutItem operation:
The conditional request failed

Explanation

Silence and exit 0 are the only success signal. put-item prints no JSON unless you ask for --return-values, so a script that greps stdout for confirmation will never fire. Check $?. Running the command above twice on aws-cli/2.36.9 gave:

first run:   (no output)                exit 0
second run:  aws: [ERROR]: An error occurred (ConditionalCheckFailedException) when calling the PutItem operation: The conditional request failed
             exit 254

254 is "the service said no", not "the CLI broke". The AWS CLI reserves 252/253 for its own syntax and config problems and 255 for everything else, so a ConditionalCheckFailedException, a ValidationException and a throttle all land on the same 254. If your script needs to tell an expected condition failure from a real fault, parse the error name, not the exit code. Note also that 2.36.9 prefixes the message with aws: [ERROR]: , which older builds did not; a regex anchored at ^An error occurred will silently stop matching after a CLI upgrade.

A failed conditional write still costs you. The condition is evaluated by the service after it has located the item, and AWS is explicit that "if the expression evaluates to false, DynamoDB still consumes write capacity units from the table" (fetched 2026-07-28). A retry loop around a create-only put bills every attempt. For scale, --return-consumed-capacity TOTAL on a successful put of a ~15 KB item reported "CapacityUnits": 15. Writes round to 1 KB, not the 4 KB reads use.

--return-values-on-condition-check-failure works, but the CLI hides the answer. This is the flag that tells you which item blocked the write, without a second read. Add it and 2.36.9 prints:

aws: [ERROR]: An error occurred (ConditionalCheckFailedException) when calling the PutItem operation: The conditional request failed

Additional error details:
Item: <complex value>
Use "--cli-error-format json" or another error format to see the full details.

The item is in the response the whole time; the default error formatter refuses to render it. Add --cli-error-format json to get it. (--return-values ALL_OLD is the unconditional cousin and only fires on success; ReturnValues covers the five options.)

Quoting is the other half of the job. The --item argument is one shell token containing JSON containing quoted numbers ({"N": "1994"}, never 1994). Anything with an apostrophe in it, and any item past a few hundred bytes, is easier as --item file://song.json. --cli-input-json file://request.json goes further and takes the entire request, condition expression included, which is also the form you can diff in review.

The aliases are not optional decoration. #cond0/#cond1 resolve to Artist/SongTitle through --expression-attribute-names. Writing the names inline works right up until one of them collides with a reserved word, at which point the command fails on a name you did not change.

Do it visually

Hand-typing typed JSON for --item is where most of these commands die. The free DynamoDB JSON converter takes ordinary JSON and returns the {"S": …} / {"N": …} form the flag wants, ready to save as the file:// payload.

To add and edit items against your own tables — a form per attribute, type pickers, copy the result back out as a CLI command — download DynoTable.

References

Reproduced 2026-07-28 with aws-cli/2.36.9 against DynamoDB Local (amazon/dynamodb-local) on port 9000. The exit codes, the error text and the capacity reading are captured output. The failed-write capacity claim is quoted from the AWS docs rather than measured: DynamoDB Local returns no ConsumedCapacity on the condition-failure path.

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.