How different is DynamoDB Local from DynamoDB?
DynamoDB Local 3.3.2 enforced the 8 limits we probed at the same values as DynamoDB, and it mostly raised the same error names. More than half of its error messages differed, though, and it accepted a request signed with the wrong secret.
We sent the same requests to DynamoDB Local and to Amazon DynamoDB in us-east-1 and compared the answers. Three lines of AWS's usage notes turned out to be wrong for 3.3.2.
We ran 76 checks on both engines. 28 came back the same, 43 came back different, and 5 reached an API that DynamoDB Local does not implement.
| Group | Checks | Same |
|---|---|---|
| Behavior probes | 10 | 4 |
| Limits, probed from both sides | 8 | 1 |
| Error responses | 58 | 23 |
Three lines of AWS's usage notes are wrong for 3.3.2
AWS's usage notes for DynamoDB Local list how it differs from the service. We read them on October 27, 2026, and tested the lines we could test cheaply.
- PartiQL
Limitworks. The notes say Local ignoresLimitinExecuteStatement. With 20 items under one key andLimit: 5, both engines returned 5 items and aNextToken. AWS's release history agrees: version 1.22.0, from June 2023, added "Support for Limit Operation". - Parallel scan segments work. The notes say Local ignores
SegmentandTotalSegments. Across 4 segments, both engines returned 100 items with 100 distinct keys. If Local ignored the segments, each one would return all 100. We read the segments one after another, so this does not show whether Local scans them in parallel. BillingModeSummaryis there. The notes say Local always returns null for it. For our on-demand table, Local returnedPAY_PER_REQUEST, as the service did. We did not test a provisioned table.
What differs, and what to change in your tests
- Error messages. More than half differ. The service starts many validation messages with
1 validation error detected:, and Local often states the rule in its own words. Assert on the error name, for exampleexpect(err.name).toBe('ConditionalCheckFailedException'), not on the message. - Two error names. A duplicate PartiQL
INSERTraisedDuplicateItemon Local andDuplicateItemExceptionon the service. An unknown access key ID raisedResourceNotFoundExceptionon Local andUnrecognizedClientExceptionon the service. If your code branches on either name, test that branch against DynamoDB. - Credentials. Local accepted a request signed with the wrong secret. An unknown access key ID failed as a missing table:
Cannot do operations on a non-existent table. Do not test credential handling against Local. - One database per access key ID. Local kept separate tables for each access key ID, even under
-inMemory. Use one fixed access key ID and Region in tests, or start Local with-sharedDb, as AWS documents. - Index pages fill sooner. A
KEYS_ONLYglobal secondary index query over 1,200 items of about 1 KB returned 1,014 items and aLastEvaluatedKeyon Local. The service returned all 1,200 in one page. FollowLastEvaluatedKeyinstead of counting one page. - Table names that differ only in case. Creating
Authorsand thenauthorsfailed on Local withInternalFailureand HTTP status 500. A retrying client treats that as a service fault. The service created both tables. - Table ARNs. Local's ARN reads
arn:aws:dynamodb:ddblocal:000000000000:table/…. Do not read the Region or account from an ARN in code you test on Local. - Idempotency tokens. Local accepted a reused
ClientRequestTokenwith a different payload. The service raisedIdempotentParameterMismatchException. - Features Local leaves out. Local accepted a vector index in
CreateTableand then dropped it. It returned no item collection metrics on a table with an LSI. It answered tagging, exports and the 2017-version global table APIs withUnknownOperationException. Test these against DynamoDB. - Image versions. A cached
latestimage on one of our machines was still 3.3.0. Pin the version.
To give every access key ID and Region one database:
docker run -p 8000:8000 amazon/dynamodb-local:3.3.2 -jar DynamoDBLocal.jar -sharedDb -inMemoryConsumed capacity matched on the reads we measured: two GetItem calls and one 8-item Query. We did not check writes.
Our DynamoDB Local guide covers running it, and connecting to DynamoDB Local and LocalStack covers pointing tools at it. DynoTable connects to DynamoDB Local through a local profile. Download DynoTable to open your local tables.
How we measured
One Node process ran every check on Local first, then on the service, with the same AWS SDK for JavaScript code and the SDK's default retries. Each check ran once, so treat a count such as 1,014 as one observation. Checks share fixture tables, so state can carry from one check to the next.
Local ran from the official image, pinned by digest. The service side used on-demand scratch tables in us-east-1 that the run created and deleted.
Two answers count as the same when the error name, message and HTTP status match, or when a probe's measured values match. Table names, account IDs, access key IDs and echoed request values are replaced with placeholders on both sides. ARNs and the split of keys between scan segments are not compared.
The error checks come from our catalog of common DynamoDB errors. They are not a random sample of the API.
Engines: DynamoDB Local 3.3.2 and Amazon DynamoDB (us-east-1), captured October 27, 2026. Client: @aws-sdk/client-dynamodb 3.1126.0 on Node v24.22.1.
Local image: amazon/dynamodb-local:3.3.2@sha256:6ada631ec7aa5fc83446855eb17233e3dad184fec47477e8140c6c569cfe0c86, started with its default java -jar DynamoDBLocal.jar -inMemory.
8 error checks break the same limit as a limit probe and get the same message from the service, so they count in both groups.
This capture leaves out anything slow or costly: throttling, TTL deletes, transaction conflicts, index backfill, provisioned tables, other SDKs, other Local modes and other Regions.
Behavior probes
- PartiQL ExecuteStatement with Limit: Same
- Local: items returned: 5; NextToken set:
true - DynamoDB: items returned: 5; NextToken set:
true
- Local: items returned: 5; NextToken set:
- Parallel Scan with four segments: Same
- Local: items written: 100; items returned: 100; distinct keys: 100
- DynamoDB: items written: 100; items returned: 100; distinct keys: 100
- Two tables whose names differ only in case: Differs
- Local: first CreateTable succeeded:
true;InternalFailure: The request processing has failed because of an unknown error, exception or failure. - DynamoDB: first CreateTable succeeded:
true
- Local: first CreateTable succeeded:
- A KEYS_ONLY index query over 1,200 items of about 1 KB: Differs
- Local: items written: 1,200; items in the first page: 1,014; LastEvaluatedKey set:
true - DynamoDB: items written: 1,200; items in the first page: 1,200; LastEvaluatedKey set:
false
- Local: items written: 1,200; items in the first page: 1,014; LastEvaluatedKey set:
- DescribeTable BillingModeSummary and TableArn on an on-demand table: Same
- Local: BillingModeSummary:
PAY_PER_REQUEST - DynamoDB: BillingModeSummary:
PAY_PER_REQUEST
- Local: BillingModeSummary:
- Tag a table and read the tags back: Not in Local
- Local: TagResource:
UnknownOperationException: Tagging is not currently supported in DynamoDB Local.; ListTagsOfResource:UnknownOperationException: Tagging is not currently supported in DynamoDB Local. - DynamoDB: TagResource: succeeds; ListTagsOfResource: succeeds, tags
team=parity
- Local: TagResource:
- CreateTable with a vector index: Differs
- Local: vector indexes in DescribeTable:
null - DynamoDB: vector indexes in DescribeTable:
Embeddings
- Local: vector indexes in DescribeTable:
- An access key id containing an underscore: Differs
- Local:
UnrecognizedClientException: The Access Key ID or security token is invalid. - DynamoDB:
UnrecognizedClientException: The security token included in the request is invalid.
- Local:
- ReturnItemCollectionMetrics on a table with an LSI: Differs
- Local: ItemCollectionMetrics:
null - DynamoDB: ItemCollectionMetrics:
{"ItemCollectionKey":{"pk":{"S":"parity-icm"}},"SizeEstimateRangeGB":[0,1]}
- Local: ItemCollectionMetrics:
- ConsumedCapacity reported for reads: Same
- Local: GetItem, eventually consistent: 0.5; GetItem, strongly consistent: 1; Query of 8 items of about 1 KB, strongly consistent: 2
- DynamoDB: GetItem, eventually consistent: 0.5; GetItem, strongly consistent: 1; Query of 8 items of about 1 KB, strongly consistent: 2
Eight limits, probed from both sides
Both engines accepted a request at each of the 8 documented limits and rejected one unit past it.
| Limit | Value | Error one past the limit | Message |
|---|---|---|---|
| Maximum item size | 409,600 bytes | ValidationException | Same |
| Maximum partition key value | 2,048 bytes | ValidationException | Differs |
| Maximum sort key value | 1,024 bytes | ValidationException | Differs |
| Maximum nesting depth | 32 levels | ValidationException | Differs |
| Maximum expression length | 4,096 bytes | ValidationException | Differs |
| Maximum items per BatchWriteItem | 25 items | ValidationException | Differs |
| Maximum keys per BatchGetItem | 100 items | ValidationException | Differs |
| Maximum actions per TransactWriteItems | 100 items | ValidationException | Differs |
Every error check
5 with a different error name, or an error on one engine only
duplicateitemexception- Local:
DuplicateItem: Duplicate primary key exists in table - DynamoDB:
DuplicateItemException: Duplicate primary key exists in table
- Local:
idempotentparametermismatchexception- Local: succeeds
- DynamoDB:
IdempotentParameterMismatchException: Specified idempotent token was used with different request parameters within the idempotency window
security-token-invalid- Local:
ResourceNotFoundException: Cannot do operations on a non-existent table - DynamoDB:
UnrecognizedClientException: The security token included in the request is invalid.
- Local:
signature-does-not-match- Local: succeeds
- DynamoDB:
InvalidSignatureException: The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.
vector-index-update-on-local- Local:
ValidationException: Nothing to update - DynamoDB: succeeds
- Local:
18 with the same error name and different wording
batchgetitem-too-many-items,ValidationException- Local:
Too many items requested for the BatchGetItem call - DynamoDB:
1 validation error detected: Value at 'RequestItems.<table-name>.member.Keys' failed to satisfy constraint: Member must have length less than or equal to 100
- Local:
batchwriteitem-too-many-items,ValidationException- Local:
Too many items requested for the BatchWriteItem call - DynamoDB:
1 validation error detected: Value '<your request>' at 'requestItems' failed to satisfy constraint: Map value must satisfy constraint: [Member must have length less than or equal to 25, Member must have length greater than or equal to 1]
- Local:
cannot-do-operations-on-a-non-existent-table,ResourceNotFoundException- Local:
Cannot do operations on a non-existent table - DynamoDB:
Requested resource not found
- Local:
dynamodb-query-condition-missed-key-schema-element,ValidationException- Local:
Query condition missed key schema element - DynamoDB:
Query condition missed key schema element: pk
- Local:
expression-size-exceeded,ValidationException- Local:
Invalid ConditionExpression: Expression size has exceeded the maximum allowed size; expression size: 27785 - DynamoDB:
1 validation error detected: Invalid ConditionExpression: Expression size has exceeded the maximum allowed size;
- Local:
gsi-count-exceeded,ValidationException- Local:
GlobalSecondaryIndex count exceeds the per-table limit - DynamoDB:
One or more parameter values were invalid: GlobalSecondaryIndex count exceeds the per-table limit of 20
- Local:
item-size-greater-than-key-limit,ValidationException- Local:
Hash primary key values must be under 2048 bytes, and range primary key values must be under 1024 bytes - DynamoDB:
One or more parameter values were invalid: Aggregated size of all range keys has exceeded the size limit of 1024 bytes
- Local:
key-attribute-size-exceeded,ValidationException- Local:
Hash primary key values must be under 2048 bytes, and range primary key values must be under 1024 bytes - DynamoDB:
One or more parameter values were invalid: Size of hashkey has exceeded the maximum size limit of2048 bytes
- Local:
key-element-does-not-match-schema,ValidationException- Local:
One or more parameter values were invalid: Type mismatch for key - DynamoDB:
The provided key element does not match the schema
- Local:
lsi-count-exceeded,ValidationException- Local:
Cannot have more than 5 local secondary indexes per table - DynamoDB:
One or more parameter values were invalid: Number of LocalSecondaryIndexes exceeds per-table limit of 5
- Local:
projected-attributes-per-index-exceeded,ValidationException- Local:
No more than 20 attributes per table can be projected into Local Secondary Indices - DynamoDB:
1 validation error detected: Value '<your request>' at 'globalSecondaryIndexes.1.member.projection.nonKeyAttributes' failed to satisfy constraint: Member must have length less than or equal to 20
- Local:
projected-attributes-per-table-exceeded,ValidationException- Local:
No more than 20 attributes per table can be projected into Local Secondary Indices - DynamoDB:
One or more parameter values were invalid: Number of projected attributes in all indexes exceeds limit of 100, number of projected attributes:120
- Local:
provided-starting-key-invalid,ValidationException- Local:
Exclusive Start Key must have same size as table's key schema - DynamoDB:
The provided starting key is invalid
- Local:
resourceinuseexception,ResourceInUseException- Local:
Cannot create preexisting table - DynamoDB:
Table already exists: <table-name>
- Local:
resourcenotfoundexception,ResourceNotFoundException- Local:
Cannot do operations on a non-existent table - DynamoDB:
Requested resource not found: Table: <table-name> not found
- Local:
scan-segment-out-of-range,ValidationException- Local:
1 validation error detected: Value '<your request>' at 'segment' failed to satisfy constraint: Member must have value less than or equal to 1 - DynamoDB:
The Segment parameter is zero-based and must be less than parameter TotalSegments
- Local:
table-already-exists,ResourceInUseException- Local:
Cannot create preexisting table - DynamoDB:
Table already exists: <table-name>
- Local:
transactwriteitems-too-many,ValidationException- Local:
Member must have length less than or equal to 100 - DynamoDB:
1 validation error detected: Value '<your request>' at 'transactItems' failed to satisfy constraint: Member must have length less than or equal to 100
- Local:
8 where the service only adds 1 validation error detected:
Otherwise the message is the same. This is the message from DynamoDB Local:
expressionattributenames-contains-invalid-key:ValidationException: ExpressionAttributeNames contains invalid key: Syntax error; key: "#my.attribute"expressionattributenames-not-used:ValidationException: Value provided in ExpressionAttributeNames unused in expressions: keys: {#status}expressionattributevalues-not-used:ValidationException: Value provided in ExpressionAttributeValues unused in expressions: keys: {:status}nesting-depth-exceeded:ValidationException: Nesting Levels have exceeded supported limits: Attributes in the item have nested levels beyond supported limitnumber-overflow:ValidationException: Number overflow. Attempting to store a number with magnitude larger than supported rangereserved-keyword-attribute-name:ValidationException: Invalid UpdateExpression: Attribute name is a reserved keyword; reserved keyword: statusselect-count-with-projection:ValidationException: Cannot specify the ProjectionExpression when choosing to get only the Counttwo-document-paths-overlap:ValidationException: Invalid UpdateExpression: Two document paths overlap with each other; must remove or rewrite one of these paths; path one: [profile], path two: [profile, city]
4 that reach an API Local does not implement
Local answered all 4 with UnknownOperationException: An unknown operation was requested. The service answered:
export-no-pitr:PointInTimeRecoveryUnavailableException: Point in time recovery is not enabled for table '<table-name>'globaltablenotfoundexception:GlobalTableNotFoundException: Global table not found: Global table with name: 'no-such-global-table' does not exist.globaltablenotfoundexception-replica:GlobalTableNotFoundException: Global table not found: Global table with name: 'no-such-global-table' does not exist.pointintimerecoveryunavailableexception:PointInTimeRecoveryUnavailableException: Point in time recovery is not enabled for table '<table-name>'
23 that are the same
both-keyconditions-and-keyconditionexpression:ValidationException: Can not use both expression and non-expression parameters in the same request: Non-expression parameters: {KeyConditions} Expression parameters: {KeyConditionExpression}conditional-request-failed-transact:TransactionCanceledException: Transaction cancelled, please refer cancellation reasons for specific reasons [ConditionalCheckFailed], reasonsConditionalCheckFailedconditionalcheckfailedexception:ConditionalCheckFailedException: The conditional request failedconsistent-reads-not-supported-on-gsi:ValidationException: Consistent reads are not supported on global secondary indexesdocument-path-invalid-for-update:ValidationException: The document path provided in the update expression is invalid for updateduplicate-item-in-batchwrite:ValidationException: Provided list of item keys contains duplicatesduplicate-keys-in-batchget:ValidationException: Provided list of item keys contains duplicatesfilter-expression-with-key-attribute:ValidationException: Filter Expression can only contain non-primary key attributes: Primary key attribute: pkindex-not-found:ValidationException: The table does not have the specified index: no-such-indexitem-size-exceeded-maximum:ValidationException: Item size has exceeded the maximum allowed sizeitem-size-exceeded-maximum-update:ValidationException: Item size to update has exceeded the maximum allowed sizekey-schema-not-projected:ValidationException: One or more parameter values were invalid: Global secondary index gsi1 does not project [unprojected]partiql-aggregate-function:ValidationException: Unexpected path component at 1:8:3partiql-group-by:ValidationException: Unsupported clause: GROUP BYpartiql-multi-table-join:ValidationException: Only select from a single table or index is supported.partiql-subquery-in:ValidationException: IN operator must have a left hand argument of type Variable Reference and right hand argument of type Seq with at least one memberpartiql-unexpected-from-source:ValidationException: Unexpected from sourcequery-key-condition-not-supported:ValidationException: Query key condition not supportedsupplied-attributevalue-empty:ValidationException: Supplied AttributeValue is empty, must contain exactly one of the supported datatypestransaction-multiple-operations-one-item:ValidationException: Transaction request cannot include multiple operations on one itemtransactioncanceledexception:TransactionCanceledException: Transaction cancelled, please refer cancellation reasons for specific reasons [ConditionalCheckFailed], reasonsConditionalCheckFailedupdate-expression-operand-incorrect-datatype:ValidationException: An operand in the update expression has an incorrect data typevalidation-empty-expression:ValidationException: Invalid FilterExpression: The expression can not be empty;
Changelog
- 2026-10-27: first capture, DynamoDB Local 3.3.2.
We will run the capture again for each new DynamoDB Local release and list what changed here.