By Dmitriy · · 12 min read

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.

GroupChecksSame
Behavior probes104
Limits, probed from both sides81
Error responses5823

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.

  • Limit works. The notes say Local ignores Limit in ExecuteStatement. With 20 items under one key and Limit: 5, both engines returned 5 items and a NextToken. AWS's release history agrees: version 1.22.0, from June 2023, added "Support for Limit Operation".
  • segments work. The notes say Local ignores Segment and TotalSegments. 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.
  • BillingModeSummary is there. The notes say Local always returns null for it. For our on-demand table, Local returned PAY_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 example expect(err.name).toBe('ConditionalCheckFailedException'), not on the message.
  • Two error names. A duplicate PartiQL INSERT raised DuplicateItem on Local and DuplicateItemException on the service. An unknown access key ID raised ResourceNotFoundException on Local and UnrecognizedClientException on 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_ONLY query over 1,200 items of about 1 KB returned 1,014 items and a LastEvaluatedKey on Local. The service returned all 1,200 in one page. Follow LastEvaluatedKey instead of counting one page.
  • Table names that differ only in case. Creating Authors and then authors failed on Local with InternalFailure and 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 ClientRequestToken with a different payload. The service raised IdempotentParameterMismatchException.
  • Features Local leaves out. Local accepted a vector index in CreateTable and then dropped it. It returned no item collection metrics on a table with an . It answered tagging, exports and the 2017-version global table APIs with UnknownOperationException. Test these against DynamoDB.
  • Image versions. A cached latest image 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 -inMemory

Consumed 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
  • 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
  • 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
  • DescribeTable BillingModeSummary and TableArn on an on-demand table: Same
    • Local: BillingModeSummary: PAY_PER_REQUEST
    • DynamoDB: BillingModeSummary: PAY_PER_REQUEST
  • 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
  • CreateTable with a vector index: Differs
    • Local: vector indexes in DescribeTable: null
    • DynamoDB: vector indexes in DescribeTable: Embeddings
  • 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.
  • ReturnItemCollectionMetrics on a table with an LSI: Differs
    • Local: ItemCollectionMetrics: null
    • DynamoDB: ItemCollectionMetrics: {"ItemCollectionKey":{"pk":{"S":"parity-icm"}},"SizeEstimateRangeGB":[0,1]}
  • 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.

LimitValueError one past the limitMessage
Maximum item size409,600 bytesValidationExceptionSame
Maximum partition key value2,048 bytesValidationExceptionDiffers
Maximum sort key value1,024 bytesValidationExceptionDiffers
Maximum nesting depth32 levelsValidationExceptionDiffers
Maximum expression length4,096 bytesValidationExceptionDiffers
Maximum items per BatchWriteItem25 itemsValidationExceptionDiffers
Maximum keys per BatchGetItem100 itemsValidationExceptionDiffers
Maximum actions per TransactWriteItems100 itemsValidationExceptionDiffers

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
  • 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.
  • 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

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
  • 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]
  • cannot-do-operations-on-a-non-existent-table, ResourceNotFoundException
    • Local: Cannot do operations on a non-existent table
    • DynamoDB: Requested resource not found
  • dynamodb-query-condition-missed-key-schema-element, ValidationException
    • Local: Query condition missed key schema element
    • DynamoDB: Query condition missed key schema element: pk
  • 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;
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • resourceinuseexception, ResourceInUseException
    • Local: Cannot create preexisting table
    • DynamoDB: Table already exists: <table-name>
  • resourcenotfoundexception, ResourceNotFoundException
    • Local: Cannot do operations on a non-existent table
    • DynamoDB: Requested resource not found: Table: <table-name> not found
  • 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
  • table-already-exists, ResourceInUseException
    • Local: Cannot create preexisting table
    • DynamoDB: Table already exists: <table-name>
  • 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

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 limit
  • number-overflow: ValidationException: Number overflow. Attempting to store a number with magnitude larger than supported range
  • reserved-keyword-attribute-name: ValidationException: Invalid UpdateExpression: Attribute name is a reserved keyword; reserved keyword: status
  • select-count-with-projection: ValidationException: Cannot specify the ProjectionExpression when choosing to get only the Count
  • two-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], reasons ConditionalCheckFailed
  • conditionalcheckfailedexception: ConditionalCheckFailedException: The conditional request failed
  • consistent-reads-not-supported-on-gsi: ValidationException: Consistent reads are not supported on global secondary indexes
  • document-path-invalid-for-update: ValidationException: The document path provided in the update expression is invalid for update
  • duplicate-item-in-batchwrite: ValidationException: Provided list of item keys contains duplicates
  • duplicate-keys-in-batchget: ValidationException: Provided list of item keys contains duplicates
  • filter-expression-with-key-attribute: ValidationException: Filter Expression can only contain non-primary key attributes: Primary key attribute: pk
  • index-not-found: ValidationException: The table does not have the specified index: no-such-index
  • item-size-exceeded-maximum: ValidationException: Item size has exceeded the maximum allowed size
  • item-size-exceeded-maximum-update: ValidationException: Item size to update has exceeded the maximum allowed size
  • key-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:3
  • partiql-group-by: ValidationException: Unsupported clause: GROUP BY
  • partiql-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 member
  • partiql-unexpected-from-source: ValidationException: Unexpected from source
  • query-key-condition-not-supported: ValidationException: Query key condition not supported
  • supplied-attributevalue-empty: ValidationException: Supplied AttributeValue is empty, must contain exactly one of the supported datatypes
  • transaction-multiple-operations-one-item: ValidationException: Transaction request cannot include multiple operations on one item
  • transactioncanceledexception: TransactionCanceledException: Transaction cancelled, please refer cancellation reasons for specific reasons [ConditionalCheckFailed], reasons ConditionalCheckFailed
  • update-expression-operand-incorrect-datatype: ValidationException: An operand in the update expression has an incorrect data type
  • validation-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.

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.