Summary
TransactGetItems with a missing partition key in an action's Key returns HTTP 500 InternalServerError("Missing partition key") instead of TransactionCanceledException carrying a ValidationError cancellation reason. Two layered bugs:
- The action-level validation surfaces as
InternalServerError (server fault) rather than a 4xx validation error. The duplicate-target dedup loop calls extract_key_strings (which throws InternalServerError on missing PK) before validate_key_only (which would throw the proper ValidationException) gets a chance to run.
- Even with the right exception class, real DynamoDB wraps per-action validation failures inside
TransactionCanceledException with a positional CancellationReasons array. The reason Code is ValidationError, not ConditionalCheckFailed. Dynoxide's transact-get path doesn't have this wrapping at all.
Version: dynoxide 0.9.9
Test: tests/tier3/error-messages/transactGetItems.test.ts "missing key attribute: action-level ValidationError surfaces as TransactionCanceledException"
Expected:
- Exception class:
TransactionCanceledException
- Message:
Transaction cancelled, please refer cancellation reasons for specific reasons [ValidationError]
CancellationReasons[].Code: ['ValidationError']
Actual:
- HTTP 500
- Exception class:
InternalServerError
- Message:
Missing partition key
Reproduction
npx dynoxide@0.9.9 --port 8001 &
node -e '
import("@aws-sdk/client-dynamodb").then(async ({ DynamoDBClient, CreateTableCommand, TransactGetItemsCommand }) => {
const c = new DynamoDBClient({ endpoint: "http://localhost:8001", region: "us-east-1", credentials: { accessKeyId: "f", secretAccessKey: "f" } });
const t = "t" + Date.now();
await c.send(new CreateTableCommand({ TableName: t, AttributeDefinitions: [{ AttributeName: "pk", AttributeType: "S" }], KeySchema: [{ AttributeName: "pk", KeyType: "HASH" }], BillingMode: "PAY_PER_REQUEST" }));
try { await c.send(new TransactGetItemsCommand({ TransactItems: [{ Get: { TableName: t, Key: {} } }] })); }
catch (e) { console.log(e.name, e.$response?.statusCode, e.message); }
});
'
Summary
TransactGetItemswith a missing partition key in an action'sKeyreturns HTTP 500InternalServerError("Missing partition key")instead ofTransactionCanceledExceptioncarrying aValidationErrorcancellation reason. Two layered bugs:InternalServerError(server fault) rather than a 4xx validation error. The duplicate-target dedup loop callsextract_key_strings(which throwsInternalServerErroron missing PK) beforevalidate_key_only(which would throw the properValidationException) gets a chance to run.TransactionCanceledExceptionwith a positionalCancellationReasonsarray. The reasonCodeisValidationError, notConditionalCheckFailed. Dynoxide's transact-get path doesn't have this wrapping at all.Version: dynoxide 0.9.9
Test:
tests/tier3/error-messages/transactGetItems.test.ts"missing key attribute: action-level ValidationError surfaces as TransactionCanceledException"Expected:
TransactionCanceledExceptionTransaction cancelled, please refer cancellation reasons for specific reasons [ValidationError]CancellationReasons[].Code:['ValidationError']Actual:
InternalServerErrorMissing partition keyReproduction