Skip to content

DynamoDB Local

d89f8fcc6b1a 29 recorded runs

83.8%
correct, across 941 of 954 operations · 13 unsupported
Tier 1 92.2%
Tier 2 82.9%
Tier 3 71.6%

Per-region conformance

The headline is 27 regions. Real DynamoDB doesn't behave identically in every region, so a target can match some regions more closely than others. eu-west-2 is marked as the historical baseline, and a region that couldn't be resolved this sweep is flagged rather than counted as a disagreement.

83.8% 27 regions
af-south-1ap-east-1ap-northeast-1ap-northeast-3ap-south-1ap-south-2ap-southeast-2ap-southeast-3ap-southeast-4ap-southeast-5ap-southeast-6ap-southeast-7ca-central-1ca-west-1eu-central-2eu-south-1eu-south-2eu-west-1eu-west-3il-central-1me-central-1mx-central-1sa-east-1us-east-1us-east-2us-west-1us-west-2
83.7% 6 regions
ap-east-2ap-northeast-2ap-southeast-1eu-central-1eu-north-1eu-west-2 · baseline

Fully supports

Operation areas this target implements completely - every test passes, nothing skipped. Areas it only partly implements, or gets wrong, are in “Where it falls short” below.

  • batchGetItem tier 1
  • deleteItem tier 1
  • deleteTable tier 1
  • getItem tier 1
  • listTables tier 1
  • scan tier 1
  • streams tier 2
  • ttl tier 2

Conformance over time

DynamoDB Local conformance history, 2026-03-23 to 2026-07-18 75% 88% 100% 23 Mar 25 Apr 23 May 25 May 29 May 16 Jun 19 Jun 22 Jun 24 Jun 29 Jun 1 Jul 6 Jul 14 Jul 16 Jul 18 Jul 83.8%
Now 83.8% on 18 Jul, lowest 81.9% on 30 Jun.

By operation

Every operation this target implements, with the share of its tests that pass. full, partial, failing, unsupported. The matrix compares operations across targets.

Tier 1 - Core

  • batchGetItem 15/15 100.0% batchGetItem: supported (15 pass)
  • batchWriteItem 15/18 83.3% batchWriteItem: partially supported (15 pass, 3 fail)
  • createTable 26/30 86.7% createTable: partially supported (26 pass, 4 fail)
  • deleteItem 13/13 100.0% deleteItem: supported (13 pass)
  • deleteTable 3/3 100.0% deleteTable: supported (3 pass)
  • describeTable 3/4 75.0% describeTable: partially supported (3 pass, 1 fail)
  • getItem 40/40 100.0% getItem: supported (40 pass)
  • listTables 5/5 100.0% listTables: supported (5 pass)
  • putItem 91/116 78.4% putItem: partially supported (91 pass, 25 fail)
  • query 88/90 97.8% query: partially supported (88 pass, 2 fail)
  • scan 56/56 100.0% scan: supported (56 pass)
  • updateItem 69/70 98.6% updateItem: partially supported (69 pass, 1 fail)
  • updateTable 14/15 93.3% updateTable: partially supported (14 pass, 1 fail)

Tier 2 - Complete

  • account 1/1 · 1 skip 100.0% account: partially supported (1 pass, 1 skip)
  • backups 0/0 · 5 skip n/a backups: unsupported (5 skip)
  • contributorInsights 0/0 · 2 skip n/a contributorInsights: unsupported (2 skip)
  • export 0/0 · 2 skip n/a export: unsupported (2 skip)
  • kinesis 0/0 · 1 skip n/a kinesis: unsupported (1 skip)
  • partiql 33/36 91.7% partiql: partially supported (33 pass, 3 fail)
  • resourcePolicy 0/0 · 2 skip n/a resourcePolicy: unsupported (2 skip)
  • streams 18/18 100.0% streams: supported (18 pass)
  • tags 0/8 0.0% tags: failing (8 fail)
  • transactions 51/62 82.3% transactions: partially supported (51 pass, 11 fail)
  • ttl 7/7 100.0% ttl: supported (7 pass)
  • updateTable 11/14 78.6% updateTable: partially supported (11 pass, 3 fail)

Tier 3 - Strict

  • error-messages 90/154 58.4% error-messages: partially supported (90 pass, 64 fail)
  • legacy-api 41/42 97.6% legacy-api: partially supported (41 pass, 1 fail)
  • limits 91/93 97.8% limits: partially supported (91 pass, 2 fail)
  • validation-ordering 7/31 22.6% validation-ordering: partially supported (7 pass, 24 fail)

Where it falls short

Operation groups with gaps in the latest run, biggest first. Unsupported means the target doesn't implement that feature at all, often by design. Open one for the exact tests, or see the conformance suite.

  • error-messages Tier 3 64 failing
    View these tests in the suite →
    • BatchGetItem - exact error messages empty RequestItems: full required-parameter error
    • BatchGetItem - exact error messages > 100 keys across all tables: interpolated full error
    • BatchGetItem - exact error messages non-existent table: full ResourceNotFoundException message
    • BatchWriteItem - exact error messages empty RequestItems: full required-parameter error
    • BatchWriteItem - exact error messages > 25 requests: anchored regex on the constraint phrase
    • BatchWriteItem - exact error messages non-existent table: full ResourceNotFoundException message
    • BatchWriteItem - exact error messages wrong-typed table key: full schema-mismatch message
    • BatchWriteItem - exact error messages non-scalar table key: full schema-mismatch message
    • BatchWriteItem - exact error messages wrong-typed index key: full type-mismatch message
    • BatchWriteItem - exact error messages non-scalar index key: full type-mismatch message
    • BatchWriteItem - exact error messages empty-string index key: full secondary-index-key message
    • CreateTable - exact error messages missing TableName: full required-parameter error
    • CreateTable - exact error messages short table name (2 chars): minimum length 3 error
    • CreateTable - exact error messages duplicate attribute in KeySchema
    • CreateTable - exact error messages more than 2 KeySchema elements
    • CreateTable - exact error messages invalid KeyType
    • CreateTable - exact error messages invalid AttributeType
    • CreateTable - exact error messages LSI without range key on base table
    • CreateTable - exact error messages duplicate index names
    • CreateTable - exact error messages PAY_PER_REQUEST with ProvisionedThroughput: full conflict message
    • CreateTable - exact error messages GSI INCLUDE projection without NonKeyAttributes: full missing-attributes message
    • CreateTable - exact error messages LSI INCLUDE projection without NonKeyAttributes: full missing-attributes message
    • CreateTable - exact error messages StreamSpecification StreamEnabled:false with a StreamViewType: full conflict message
    • DeleteItem - exact error messages non-existent table: full ResourceNotFoundException message
    • DeleteItem - exact error messages malformed Key (missing range key on composite table): full schema-mismatch error
    • GetItem - exact error messages non-existent table: full ResourceNotFoundException message
    • GetItem - exact error messages malformed Key (missing range key on composite table): full schema-mismatch error
    • PutItem - exact error messages missing table name: full validation error string
    • PutItem - exact error messages empty table name: minimum length 1 error
    • PutItem - exact error messages table name too long (256 chars): maximum length 255 error
    • PutItem - exact error messages table name with invalid chars: regex pattern error
    • PutItem - exact error messages empty string member in NS: numeric conversion error
    • Query - exact error messages missing hash key in KeyConditionExpression
    • Query - exact error messages non-key attribute in KeyConditionExpression
    • Query - exact error messages invalid Select value
    • Query - exact error messages Limit of 0
    • Query - exact error messages malformed ExclusiveStartKey: short invalid-starting-key error
    • Query - exact error messages Select SPECIFIC_ATTRIBUTES without ProjectionExpression: full required-projection message
    • Scan - exact error messages Segment without TotalSegments: full required-parameter error
    • Scan - exact error messages Segment >= TotalSegments: full out-of-range error
    • Scan - exact error messages TotalSegments without Segment: full required-parameter error
    • Scan - exact error messages Limit of 0: full minimum-value error
    • Scan - exact error messages non-existent table: full ResourceNotFoundException message
    • Scan - exact error messages malformed ExclusiveStartKey: long schema-mismatch error
    • TransactGetItems - exact error messages empty TransactItems: full minimum-length error
    • TransactGetItems - exact error messages > 100 gets: anchored regex on the constraint phrase
    • TransactGetItems - exact error messages non-existent table: full ResourceNotFoundException message
    • TransactGetItems - exact error messages missing key attribute: action-level ValidationError surfaces as TransactionCanceledException
    • TransactWriteItems - exact error messages empty TransactItems: full minimum-length error
    • TransactWriteItems - exact error messages > 100 actions: anchored regex on the constraint phrase
    • TransactWriteItems - exact error messages non-existent table: full ResourceNotFoundException message
    • TransactWriteItems - exact error messages Put wrong-typed table key: cancelled with full ValidationError reason
    • TransactWriteItems - exact error messages Put non-scalar table key: cancelled with full ValidationError reason
    • TransactWriteItems - exact error messages Put wrong-typed index key: cancelled with full ValidationError reason
    • TransactWriteItems - exact error messages Put non-scalar index key: cancelled with full ValidationError reason
    • TransactWriteItems - exact error messages Update wrong-typed index key: cancelled with full ValidationError reason
    • TransactWriteItems - exact error messages Update non-scalar index key: cancelled with full ValidationError reason
    • TransactWriteItems - exact error messages Put empty-string index key: top-level ValidationException
    • TransactWriteItems - exact error messages Update wrong-typed Key: cancelled with schema-mismatch reason
    • TransactWriteItems - exact error messages Update non-scalar Key: cancelled with schema-mismatch reason
    • TransactWriteItems - exact error messages Delete wrong-typed Key: cancelled with schema-mismatch reason
    • TransactWriteItems - exact error messages Delete non-scalar Key: cancelled with schema-mismatch reason
    • TransactWriteItems - exact error messages ConditionCheck wrong-typed Key: cancelled with schema-mismatch reason
    • TransactWriteItems - exact error messages ConditionCheck non-scalar Key: cancelled with schema-mismatch reason
  • putItem Tier 1 25 failing
    View these tests in the suite →
    • ReturnItemCollectionMetrics PutItem with SIZE returns ItemCollectionMetrics
    • ReturnItemCollectionMetrics DeleteItem with SIZE returns ItemCollectionMetrics
    • ReturnItemCollectionMetrics UpdateItem with SIZE returns ItemCollectionMetrics
    • PutItem - number format rejects "+e2"
    • PutItem - number format rejects "e2"
    • PutItem - number format rejects "+1+2"
    • PutItem - number format rejects "1+2"
    • PutItem - number format rejects "+1.2.3"
    • PutItem - number format rejects "1.2.3"
    • PutItem - number format rejects "++5"
    • PutItem - number format rejects "+-5"
    • PutItem - number format rejects "-+5"
    • PutItem - number format rejects "+"
    • PutItem - number format rejects "-"
    • PutItem - number format rejects "1e"
    • PutItem - number format rejects "1e+"
    • PutItem - number format rejects "."
    • PutItem - number format rejects "1.2e3.4"
    • PutItem - number format rejects "0x5"
    • PutItem - number format rejects "NaN"
    • PutItem - number format rejects "Infinity"
    • PutItem - number format rejects "1_000"
    • PutItem - number format rejects " 5"
    • PutItem - number format rejects "5 "
    • PutItem - number format rejects "1 5"
  • validation-ordering Tier 3 24 failing
    View these tests in the suite →
    • Batch operations - validation ordering BatchWriteItem rejects empty RequestItems
    • Batch operations - validation ordering BatchGetItem rejects empty RequestItems
    • CreateTable - validation ordering empty TableName reports only tableName constraint
    • CreateTable - validation ordering invalid table name pattern reports only tableName
    • CreateTable - validation ordering reports invalid BillingMode and invalid KeySchema element together
    • CreateTable - validation ordering rejects a duplicate attribute in KeySchema
    • CreateTable - validation ordering rejects more than two KeySchema elements
    • CreateTable - validation ordering rejects an invalid BillingMode on its own
    • DeleteItem - validation ordering empty TableName reports only tableName constraint
    • DeleteItem - validation ordering reports invalid ReturnValues and invalid ReturnConsumedCapacity together
    • DeleteItem - validation ordering invalid table name pattern reports only tableName
    • PutItem - validation ordering empty TableName reports tableName constraint (stops early)
    • PutItem - validation ordering empty TableName with invalid ReturnValues reports only tableName
    • PutItem - validation ordering reports invalid ReturnConsumedCapacity, ReturnItemCollectionMetrics, and ReturnValues together
    • PutItem - validation ordering reports invalid table name pattern
    • Query - validation ordering reports invalid TableName pattern
    • Query - validation ordering reports invalid ReturnConsumedCapacity
    • Scan - validation ordering rejects Segment without TotalSegments
    • Scan - validation ordering rejects Segment >= TotalSegments
    • Scan - validation ordering rejects TotalSegments without Segment
    • Scan - validation ordering rejects a negative Segment
    • UpdateItem - validation ordering empty TableName reports only tableName constraint
    • UpdateItem - validation ordering rejects invalid ReturnValues (UpdateItem reports the first enum error)
    • UpdateItem - validation ordering invalid table name pattern reports only tableName
  • transactions Tier 2 11 failing
    View these tests in the suite →
    • TransactGetItems - ConsumedCapacity INDEXES breakdown includes the table read capacity units
    • TransactGetItems - projection matching nothing omits Item when the projection matches no attribute on a present item
    • TransactWriteItems - validation Put with a non-scalar table key cancels with a ValidationError reason
    • TransactWriteItems - validation Put with a non-scalar index key cancels with a ValidationError reason
    • TransactWriteItems - validation Update setting a non-scalar index key cancels with a ValidationError reason
    • TransactWriteItems - validation Update with a non-scalar Key cancels with a ValidationError reason
    • TransactWriteItems - validation Delete with a non-scalar Key cancels with a ValidationError reason
    • TransactWriteItems - validation ConditionCheck with a non-scalar Key cancels with a ValidationError reason
    • TransactWriteItems - ConsumedCapacity charges 2 write capacity units per item
    • TransactWriteItems - ConsumedCapacity: conditional, check, replay, cancel a standalone ConditionCheck action costs 2 write capacity units
    • TransactWriteItems - ConsumedCapacity: conditional, check, replay, cancel reports write capacity on the first call and read capacity on a same-token replay
  • tags Tier 2 8 failing
    View these tests in the suite →
    • Tags - basic adds tags to a table
    • Tags - basic lists tags and verifies they match what was added
    • Tags - basic adds additional tags and verifies all tags are present
    • Tags - basic removes specific tag keys with UntagResource
    • Tags - basic verifies removed tags are gone after untag
    • Tags - basic overwrites an existing tag with the same key but different value
    • Tags - validation rejects TagResource with an invalid ARN format
    • Tags - validation rejects ListTagsOfResource with a non-existent ARN
  • backups Tier 2 unsupported
    View these tests in the suite →
    • Continuous backups - PITR reports PITR DISABLED by default
    • Continuous backups - PITR enabling PITR transitions PointInTimeRecoveryStatus to ENABLED
    • On-demand backups - lifecycle and restore CreateBackup → DescribeBackup → ListBackups → DeleteBackup
    • On-demand backups - lifecycle and restore RestoreTableFromBackup initiates a restore into a new table
    • On-demand backups - lifecycle and restore DescribeBackup on a deleted backup throws BackupNotFoundException
  • createTable Tier 1 4 failing
    View these tests in the suite →
    • CreateTable - TableId returns a TableId on CreateTable that matches the subsequent DescribeTable
    • CreateTable - validation rejects PAY_PER_REQUEST with ProvisionedThroughput specified
    • CreateTable - configuration parameters TableClass STANDARD_INFREQUENT_ACCESS round-trips
    • CreateTable - configuration parameters SSESpecification with the AWS-managed key round-trips
  • batchWriteItem Tier 1 3 failing
    View these tests in the suite →
    • BatchWriteItem - validation rejects a wrong-typed table key value
    • BatchWriteItem - validation rejects a non-scalar table key value
    • BatchWriteItem - validation rejects a non-scalar index key value
  • partiql Tier 2 3 failing
    View these tests in the suite →
    • ExecuteStatement - PartiQL rejects INSERT on an existing item (INSERT is not upsert)
    • ExecuteStatement - PartiQL returns a populated ConsumedCapacity block when requested
    • ExecuteTransaction - PartiQL idempotent replay under the same ClientRequestToken does not double-apply
  • updateTable Tier 2 3 failing
    View these tests in the suite →
    • UpdateTable - add GSI accepts a conflicting redeclaration of an existing key attribute and keeps the stored type
    • UpdateTable - add GSI drops an unused AttributeDefinition supplied with a GSI add
    • UpdateTable - GSI validation rejects a GSI keyed on a table attribute omitted from the request AttributeDefinitions
  • query Tier 1 2 failing
    View these tests in the suite →
    • Query - Limit + FilterExpression interaction rejects ExclusiveStartKey that does not match the table schema
    • Query - KeyConditionExpression semantics rejects a nested path on a key attribute
  • contributorInsights Tier 2 unsupported
    View these tests in the suite →
    • Contributor insights - enable/describe/list reports DISABLED by default
    • Contributor insights - enable/describe/list enabling transitions the status and lists the table
  • export Tier 2 unsupported
    View these tests in the suite →
    • Export and import - S3 ExportTableToPointInTime initiates an export and reports it
    • Export and import - S3 ImportTable ingests S3 data into a new table
  • resourcePolicy Tier 2 unsupported
    View these tests in the suite →
    • Resource policies - Put/Get/Delete GetResourcePolicy on a table with no policy throws PolicyNotFoundException
    • Resource policies - Put/Get/Delete Put then Get round-trips the policy, and Delete removes it
  • limits Tier 3 2 failing
    View these tests in the suite →
    • Empty values - strings, binary, and sets empty string member in NS is rejected (not a number)
    • Nesting depth - 32-level document limit rejects a 32-level ExpressionAttributeValue with ValidationException
  • describeTable Tier 1 1 failing
    View these tests in the suite →
    • DescribeTable - TableId returns a stable TableId across repeated DescribeTable calls
  • updateItem Tier 1 1 failing
    View these tests in the suite →
    • UpdateItem - ReturnValues granularity UPDATED_NEW on a nested SET returns only the changed fragment
  • updateTable Tier 1 1 failing
    View these tests in the suite →
    • UpdateTable - configuration parameters UpdateTable changes TableClass
  • account Tier 2 1 skipped
    View these tests in the suite →
    • Account reads - DescribeLimits, DescribeEndpoints DescribeEndpoints returns at least one endpoint with an address
  • kinesis Tier 2 unsupported
    View these tests in the suite →
    • Kinesis streaming destination enables a streaming destination and reports it via Describe
  • legacy-api Tier 3 1 failing
    View these tests in the suite →
    • Legacy API - AttributeUpdates (legacy UpdateExpression) AttributeUpdates with a Value and no Action defaults to PUT

Run history

Run Total Movement
83.8% 0.0pp unchanged

Suite on

Per-region scoring lands complete. 2.0.0-pre put the scoring logic in place, comparing each target against every region's recorded answer, but the evidence half was never wired: no test recorded what a target actually answered and the classifier never read one, so a fail could not be credited to a region the target matched and the score could only ever subtract. 2.0.0 closes that loop, and the seed split runs its whole lifecycle in the same release.

What changed:

  • The split tests now record what the target actually answered (src/observation-sink.ts), in the same shape the registry stores each region's answer, and the classifier carries it onto the verdict. An engine that matches a rejecting region on a split is now scored as passing in that region. Evidence only ever redeems a committed fail: a pass keeps the committed assertion's deliberate wording tolerance and is never held to the byte-exact recorded string. Committed results predate the capture, so published scores move on each target's next run, not in this change.
  • Headline ties now prefer a region the registry characterises. A region absent from every split row ties the top score by having nothing recorded about it, and the Region column must not answer "conformant to what?" with a region the suite knows nothing about. Only the label is affected; a strictly higher score still wins whatever its source.
  • The seeded { NULL: false } split closed its own loop. eu-west-2 and eu-central-1 were the last regions accepting it; by the 2026-07-17 sweep both reject it again, so every region agrees, the split is retired, and its test asserts the shared rejection. The detect, admit and reconcile path the pre-release introduced, exercised end to end within days.
  • A tooling test now asserts every tracked file is text, after a raw NUL byte used as a delimiter in one source file made grep classify the file as binary and silently skip it.
83.8% +0.2pp rose 0.2 percentage points
83.6% 0.0pp unchanged
83.6% 0.0pp unchanged
83.6% +0.9pp rose 0.9 percentage points

Suite on

The scores barely move in this release, but what they mean has changed.

Until now the suite pinned one region, eu-west-2, as ground truth. That was quietly unfair: real DynamoDB disagrees with itself in a handful of places, and a one-region baseline takes a side without saying so. The clearest case is the { NULL: false } attribute value - accepted and normalised to { NULL: true } in eu-west-2 and eu-central-1, rejected with a ValidationException in us-east-1 and ap-southeast-2. An engine matching us-east-1 on that behaviour was marked non-conformant for doing exactly what real DynamoDB does in Virginia. From 2.0.0, ground truth is per region.

What changed:

  • A weekly sweep runs the full suite against real DynamoDB in every commercial region and publishes per-region ground truth (.github/workflows/sweep.yml, ground-truth/). It gates nothing: PR scoring stays offline and deterministic.
  • Confirmed regional splits live in a checked-in registry (registry/splits.json), each row carrying what every named region actually returned, when, and who admitted it. Detection is automatic; admission is not. The sweep files an issue with the evidence, and only a human commits a row. The registry ships seeded with the { NULL: false } split.
  • Each target is scored against every observed region's expectations, and its published number is its best-matching region - named in the results table's new Region column, with the full per-region view in results/summary.json (a versioned, additive artefact; the per-target results/<slug>.json files are unchanged in shape).
  • A third result state, indeterminate, for a failed observation: a timeout, an exhausted throttle, a transport fault. It is excluded from both sides of the score and cannot become a split, a registry row, or a fail. An absent answer is not a different answer.
  • Region health is tracked in registry/regions.json. A region that cannot complete a sweep is published as unresolved rather than silently omitted; two consecutive misses drop it from the scored set and page a maintainer in the same act.

One deliberate departure from the RFC that proposed this (#75): the RFC suggested a behaviour conforms if it matches any real region. 2.0.0 scores each target against one region at a time and headlines the best match, so a target only passes a behaviour when at least one real region does what it did, and its headline reflects one coherent region rather than a mix. Match-any scoring would have accepted an engine that combines eu-west-2's answer on one behaviour with us-east-1's on another - a deployment that exists nowhere. That is stricter than the RFC asked for, and it is deliberate.

No score moves at release: the one admitted split pins eu-west-2, which is the only region in the health record until the first sweep runs. Per-target deltas will be published once the sweep admits more regions; the expected movement is roughly a tenth of a percent for the six engines that match us-east-1 on the { NULL: false } split.

The suite also grew to 954 tests, up 81, all characterised against real DynamoDB - the control-plane pins in eu-west-2, everything else across four regions (eu-west-2, eu-central-1, us-east-1, ap-southeast-2):

  • UpdateTable AttributeDefinitions reconciliation (#77, #78, #79): delta-fed GSI adds merge into the stored union rather than replacing it, a conflicting redeclaration of an existing key keeps the stored type, and deleting a GSI prunes only its orphaned key attributes. An unused definition on an add is silently dropped where CreateTable rejects the same shape.
  • ProjectionExpression validation (#81): duplicate paths, alias collisions and parent/child overlaps rejected identically on GetItem, Query, Scan and BatchGetItem, pinned with exact messages; legal shared-prefix projections guarded as accepted; GetItem's misfiled validation tests rehomed into Tier 3.
  • Expression-size limit (#80): every expression parameter caps at 4096 bytes, measured on the raw string before ExpressionAttributeNames substitution - 4096 accepted, 4097 rejected, on all five expression surfaces. No tracked target enforces this limit today, so the Tier 3 movement it causes is new coverage, not a regression.
  • Empty set members (#82): empty strings in an SS and zero-length members in a BS are accepted and round-trip intact through every write path, and contains() can find them; an empty NS member and duplicate empty members are rejected, with messages pinned. One existing assertion got stricter: the empty-binary round-trip now asserts byte length zero.
82.7% 0.0pp unchanged
82.7% 0.0pp unchanged
82.7% 0.0pp unchanged

Suite on

Grew to 873 tests, up 49, all characterised against real DynamoDB in eu-west-2. New coverage in three areas:

  • ConsumedCapacity: the transactional read/write split on a same-token replay and on ExecuteTransaction, and a correction that single-item operations report the aggregate CapacityUnits and omit the split, which is transactional-only.
  • Empty-binary key values: rejected as a top-level ValidationException on every path, including secondary-index keys and inside transactions, mirroring the empty-string rejection.
  • Expression, limit and response-shape parity: KeyConditionExpression operand and nested-path rules, ExpressionAttributeNames/Values hygiene, projection validation and list-index fidelity, reversed-bounds BETWEEN, read-path key-length and segment caps, whitespace numbers, batch unprocessed fields and cross-table projection mixing, the bare no-op upsert, multi-subpath UPDATED_NEW, filter operand ordering, hash-only-GSI pagination, and CreateTable spec validation.
82.7% +0.8pp rose 0.8 percentage points

Suite on

Grew to 824 tests, up 7, in two parts: two sibling-parity gaps where one half of a rule was pinned and the other was not, and the capacity accounting of conditional and idempotent transactional writes. All characterised against real DynamoDB in eu-west-2.

The first covers the LSI side of the INCLUDE-projection-without-NonKeyAttributes rejection; the GSI side already had it. An LSI declared with ProjectionType INCLUDE and no NonKeyAttributes is rejected as a ValidationException in tier1 and pinned to the exact message in tier3, the same wording the GSI case returns.

The second pins the Query message for Select SPECIFIC_ATTRIBUTES with no ProjectionExpression, which Scan already had. Query and Scan enforce the same rule but word it differently: Query wraps the phrase in the "1 validation error detected:" envelope, Scan returns it bare.

The transaction cases settle what a conditional TransactWriteItems actually costs. A passing condition adds no read capacity: a conditional write bills the same 2 WCU per sub-1KB item as an unconditional one, and a standalone ConditionCheck costs 2 WCU, billed as write not read. Idempotent replay splits the accounting - the first call reports 2 write capacity units, a same-token replay within the window reports 2 read capacity units for re-reading the stored result. A failing condition cancels the transaction, and the response carries no ConsumedCapacity at all. Answers #27.

81.9% -0.3pp fell 0.3 percentage points
82.2% 0.0pp unchanged
82.2% -2.3pp fell 2.3 percentage points

Suite on

Grew to 762 tests, up 18, covering a malformed value in the lookup Key of a TransactWriteItems Update, Delete, or ConditionCheck - the path a Put item key does not take. Captured across four regions (eu-west-2, us-east-1, ap-southeast-2, eu-central-1), where every string was identical, so they pin exactly. An empty-string Key surfaces as a top-level ValidationException with the same message a Put item key gives; a wrong-typed or non-scalar Key cancels with a ValidationError reason carrying "The provided key element does not match the schema" - the key-only form, not the "Type mismatch for key" message the item-key path returns. The same run confirmed the BatchWriteItem table-key schema-mismatch message is region-invariant.

84.5% -0.8pp fell 0.8 percentage points

Suite on

Grew to 744 tests, up 8, pinning the Select / ProjectionExpression rules on Query and Scan. A ProjectionExpression is only valid with Select SPECIFIC_ATTRIBUTES, and ALL_PROJECTED_ATTRIBUTES is only valid with an IndexName; real DynamoDB rejects both with a ValidationException before reading anything. The cases span ALL_ATTRIBUTES, COUNT, and ALL_PROJECTED_ATTRIBUTES, including the request that breaks both rules at once, where AWS reports the ProjectionExpression one. They assert the contractual phrase, so they hold whether or not the engine carries the wrapper AWS adds on Query but not Scan.

85.4% +0.2pp rose 0.2 percentage points

Suite on

Grew to 736 tests, up 30, covering what TransactWriteItems and BatchWriteItem do with an item whose key value is malformed - the wrong type, non-scalar, or an empty string - across both table and index keys. PutItem already covered this; the transactional and batch paths covered none of it.

Characterising it against real AWS turned up a split worth pinning. An empty-string key value is rejected by up-front input validation, so even inside a transaction it surfaces as a top-level ValidationException. A wrong-typed or non-scalar key value is caught while the transaction runs, so it comes back as a TransactionCanceledException carrying a ValidationError reason rather than a top-level error. BatchWriteItem has no cancellation path, so every variant there is a plain ValidationException. The tests pin both halves, which catches two opposite mistakes: an engine that wraps the empty-string case as a cancellation, and one that surfaces the type-mismatch case as a top-level error.

85.2% -2.0pp fell 2.0 percentage points

Suite on

Grew to 706 tests, up 7, tightening secondary-index behaviour in Tier 1. Query and Scan on a GSI or LSI now assert sparse membership: an item that omits the index key stays off the index but remains on the base table. And PutItem now rejects an item whose GSI or LSI key value is the wrong type, non-scalar, or an empty string while the base-table keys are valid, holding an index key to its declared scalar type the same way a table key is held.

87.2% +0.1pp rose 0.1 percentage points
87.0% 0.0pp unchanged
87.0% 0.0pp unchanged
87.0% 0.0pp unchanged

Suite on

Real DynamoDB in eu-west-2 reworded a chunk of its validation errors, and the Tier 3 error-message tests moved with it. They now assert the contract the error carries - its type, the field it objects to, and the constraint - rather than the exact prose, because AWS varies the wrapper, the echoed input value and the field casing from one region to the next. A four-region capture in June found eu-west-2 and eu-central-1 on the new wording and us-east-1 and ap-southeast-2 still on the old, so the line between contract and cosmetic is drawn from what is invariant across all four.

This moves some Tier 3 numbers. Targets that were only ever marked down for wording DynamoDB itself renders inconsistently now pass those checks, so their Tier 3 scores rise: the suite has stopped counting a cosmetic difference as a behavioural one. Genuine behavioural divergences are still pinned exactly, for example PutItem with a { NULL: false } attribute, which DynamoDB now accepts in eu-west-2 and normalises to { NULL: true }.

87.0% 0.0pp unchanged
87.0% 0.0pp unchanged

Suite on

Grew to 684 tests with a control-plane and table-configuration sweep: the CreateTable/UpdateTable config parameters and the secondary control-plane operations (limits, backups and PITR, exports and imports, Kinesis, resource policies, contributor insights), each characterised against real AWS and probe-skipped where a target doesn't implement it.

The published percentage changed with it. It now measures correctness over implemented operations, Pass / (Pass + Fail), so skips no longer count against the score. A skip is honest scope; a fail is a bug.

87.0% -0.1pp fell 0.1 percentage points
87.2% -0.3pp fell 0.3 percentage points

Suite on

Grew to 625 tests, up 24 on the previous run: eleven more in Tier 1 and thirteen more in Tier 3, tightening coverage of core operations and the strict edge cases.

87.5% -0.7pp fell 0.7 percentage points

Showing the 24 most recent runs. The chart above covers the full history, and every run is browsable from Runs.