Summary
DynamoDB accepts the character U+0000 (NUL) anywhere a string can appear: partition keys, sort keys, index keys, non-key string values, strings inside lists, map keys, and top-level attribute names. Measured today against the service on a scratch table; every one of eight PutItem cases returned 200 and read back byte-identical with GetItem.
ExtendDB's PostgreSQL backend returns 500 InternalServerError for every one of those cases, including a plain non-key string value. The MongoDB backend accepts NUL in keys and values but returns 500 when NUL appears in an attribute name (top level or map key). SQLite matches the service in all eight cases. A Query whose key value contains NUL also returns 500 on PostgreSQL.
Reproduction
Table pk S, sk S, GSI gsi1 on g S. Each case is one PutItem, followed by a consistent GetItem where the write succeeded.
| case |
service |
PostgreSQL |
MongoDB |
SQLite |
NUL inside pk |
200, round-trips |
500 |
200, round-trips |
200, round-trips |
NUL inside sk |
200, round-trips |
500 |
200, round-trips |
200, round-trips |
NUL inside GSI key g |
200, round-trips |
500 |
200, round-trips |
200, round-trips |
| NUL inside a non-key string |
200, round-trips |
500 |
200, round-trips |
200, round-trips |
| NUL inside a string in a list |
200, round-trips |
500 |
200, round-trips |
200, round-trips |
| NUL as the whole non-key string |
200, round-trips |
500 |
200, round-trips |
200, round-trips |
| NUL inside a map key |
200, round-trips |
500 |
500 |
200, round-trips |
| NUL inside a top-level attribute name |
200, round-trips |
500 |
500 |
200, round-trips |
Query on gsi1 with a NUL key value |
200 |
500 |
200 |
200 |
Server logs behind the 500s:
PostgreSQL, key columns: invalid byte sequence for encoding "UTF8": 0x00 (TEXT cannot hold a NUL byte).
PostgreSQL, item body: unsupported Unicode escape sequence (jsonb rejects \u0000).
MongoDB, attribute names: cstrings cannot contain null bytes (BSON field names are C strings).
The message text and error code on the wire is the generic InternalServerError: Internal server error, so a client cannot tell what was wrong with the item.
Measured on servers built from fix/postgres-key-collation (parent of the current builder work); the storage paths involved are unchanged from main.
Expected
Either behavior would be acceptable, and the first is the one that matches the service:
- Store and return U+0000 faithfully on every backend. On PostgreSQL this needs an encoding for key columns and for the jsonb body (for example a reversible escape applied on write and removed on read, or
bytea key columns). On MongoDB it needs an encoding for field names only.
- If a backend cannot store the item, reject it with a 400
ValidationException naming the limitation, consistently across every operation that accepts a string (PutItem, UpdateItem, BatchWriteItem, TransactWriteItems, Query and Scan condition values, ExpressionAttributeValues), so the caller gets a clear error instead of a 500 and the divergence is documented in docs/dynamodb-limits.md.
Option 2 is a documented divergence from the service; option 1 is fidelity. Either way the 500 is wrong.
Notes
The service's GSI Query for the NUL key returned Count 0 on the immediate read after the PutItem, which is index propagation on a fresh table, not a rejection; the base-table Scan counted all eight items.
Summary
DynamoDB accepts the character U+0000 (NUL) anywhere a string can appear: partition keys, sort keys, index keys, non-key string values, strings inside lists, map keys, and top-level attribute names. Measured today against the service on a scratch table; every one of eight PutItem cases returned 200 and read back byte-identical with GetItem.
ExtendDB's PostgreSQL backend returns
500 InternalServerErrorfor every one of those cases, including a plain non-key string value. The MongoDB backend accepts NUL in keys and values but returns 500 when NUL appears in an attribute name (top level or map key). SQLite matches the service in all eight cases. A Query whose key value contains NUL also returns 500 on PostgreSQL.Reproduction
Table
pk S, sk S, GSIgsi1ong S. Each case is one PutItem, followed by a consistent GetItem where the write succeeded.pkskggsi1with a NUL key valueServer logs behind the 500s:
PostgreSQL, key columns:
invalid byte sequence for encoding "UTF8": 0x00(TEXT cannot hold a NUL byte).PostgreSQL, item body:
unsupported Unicode escape sequence(jsonb rejects\u0000).MongoDB, attribute names:
cstrings cannot contain null bytes(BSON field names are C strings).The message text and error code on the wire is the generic
InternalServerError: Internal server error, so a client cannot tell what was wrong with the item.Measured on servers built from
fix/postgres-key-collation(parent of the current builder work); the storage paths involved are unchanged frommain.Expected
Either behavior would be acceptable, and the first is the one that matches the service:
byteakey columns). On MongoDB it needs an encoding for field names only.ValidationExceptionnaming the limitation, consistently across every operation that accepts a string (PutItem, UpdateItem, BatchWriteItem, TransactWriteItems, Query and Scan condition values, ExpressionAttributeValues), so the caller gets a clear error instead of a 500 and the divergence is documented indocs/dynamodb-limits.md.Option 2 is a documented divergence from the service; option 1 is fidelity. Either way the 500 is wrong.
Notes
The service's GSI Query for the NUL key returned Count 0 on the immediate read after the PutItem, which is index propagation on a fresh table, not a rejection; the base-table Scan counted all eight items.