{"record":{"id":"09f40f1eb5df4122","repo":"MassTransit/MassTransit","slug":"saga-update-failed-dynamodbdatabasecontext","errorCode":null,"errorMessage":"Saga update failed","messagePattern":"Saga update failed","errorType":"exception","errorClass":"SagaException","httpStatus":null,"severity":"error","filePath":"src/Persistence/MassTransit.DynamoDbIntegration/DynamoDbIntegration/Saga/DynamoDbDatabaseContext.cs","lineNumber":79,"sourceCode":"                var updateSaga = GetDynamoDbSaga(instance);\n\n                await _database.GetTargetTable<DynamoDbSaga>(new GetTargetTableConfig\n                {\n                    Conversion = _options.Config.Conversion,\n                    IsEmptyStringValueEnabled = _options.Config.IsEmptyStringValueEnabled,\n                    OverrideTableName = _options.Config.OverrideTableName,\n                    TableNamePrefix = _options.Config.TableNamePrefix\n                })\n                    .UpdateItemAsync(updateSaga.ToDocument(), new Primitive(updateSaga.CorrelationId), new Primitive(DynamoDbSaga.DefaultEntityType),\n                        operationConfig);\n            }\n            catch (ConditionalCheckFailedException)\n            {\n                throw new DynamoDbSagaConcurrencyException(\"Saga version conflict\", typeof(TSaga), instance.CorrelationId);\n            }\n            catch (Exception exception)\n            {\n                throw new SagaException(\"Saga update failed\", typeof(TSaga), instance.CorrelationId, exception);\n            }\n        }\n\n        public Task Delete(SagaConsumeContext<TSaga> context)\n        {\n            return _database.DeleteAsync(new DynamoDbSaga { CorrelationId = _options.FormatSagaKey(context.Saga.CorrelationId) }, new DeleteConfig\n            {\n                Conversion = _options.Config.Conversion,\n                IsEmptyStringValueEnabled = _options.Config.IsEmptyStringValueEnabled,\n                OverrideTableName = _options.Config.OverrideTableName,\n                SkipVersionCheck = _options.Config.SkipVersionCheck,\n                TableNamePrefix = _options.Config.TableNamePrefix\n            });\n        }\n\n        public void Dispose()\n        {\n            _database?.Dispose();","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/MassTransit/MassTransit/blob/62ab339afa3bac2e9b3fe1769d0d35d7e44778e9/src/Persistence/MassTransit.DynamoDbIntegration/DynamoDbIntegration/Saga/DynamoDbDatabaseContext.cs#L61-L97","documentation":"DynamoDbDatabaseContext<TSaga>.Update wraps any exception from the DynamoDB UpdateItemAsync call in a SagaException with message \"Saga update failed\". The optimistic-version ConditionalCheckFailedException is handled separately (as DynamoDbSagaConcurrencyException), so this error means the update failed for any other reason — AWS client errors, throttling, table access, serialization, etc. The original exception is preserved as InnerException.","triggerScenarios":"Calling Update during saga consumption when the DynamoDB UpdateItemAsync throws anything other than ConditionalCheckFailedException: e.g. AmazonDynamoDBException (table missing, throttled throughput, provisioning errors), AmazonServiceException (AWS outage/credentials), JsonException when serializing the saga, or network failures.","commonSituations":"Misconfigured AWS credentials/region so calls are rejected; the DynamoDB table or its GSI was deleted or renamed (OverrideTableName/TableNamePrefix mismatch); provisioned throughput exceeded during message bursts; saga instance not serializable with System.Text.Json options; transient network partition between the service and DynamoDB.","solutions":["Inspect exception.InnerException (the SagaException wraps the original) to identify the root AWS/serialization error","Verify the DynamoDB table exists, is active, and that Config.OverrideTableName/TableNamePrefix match the deployed table","Check AWS credentials, region, and IAM permissions (dynamodb:UpdateItem) for the running process","Enable auto-scaling or on-demand capacity / add retry policy, since throttled writes surface here","Ensure the saga type is serializable by System.Text.Json (public setters, no unsupported property types)"],"exampleFix":"// before\nawait context.Update(instance); // throws SagaException with opaque inner error\n\n// after\ntry\n{\n    await context.Update(instance);\n}\ncatch (SagaException ex)\n{\n    _logger.LogError(ex.InnerException, \"Saga {CorrelationId} update failed\", instance.CorrelationId);\n    throw;\n}","handlingStrategy":"try-catch","validationCode":"// validate before consuming\nif (string.IsNullOrWhiteSpace(_dynamoDbConfig.OverrideTableName) == false)\n    await EnsureTableExistsAsync(_dynamoDbConfig.OverrideTableName); // fail fast on missing/misconfigured table\n","typeGuard":"bool IsRetryableAwsError(Exception ex) => ex is AmazonServiceException { StatusCode: System.Net.HttpStatusCode.InternalServerError } || ex is AmazonDynamoDBException ddb && ddb.ErrorCode == \"ThrottlingException\";","tryCatchPattern":"try\n{\n    await context.Update(saga);\n}\ncatch (SagaException ex)\n{\n    _logger.LogError(ex.InnerException, \"Saga update failed for {CorrelationId}\", saga.CorrelationId);\n    throw; // let MassTransit's fault/retry pipeline handle it\n}\ncatch (DynamoDbSagaConcurrencyException)\n{\n    // version conflict: reload and reapply or skip\n}","preventionTips":["Always log SagaException.InnerException, not just the wrapper message","Add MassTransit retry policy (UseInMemoryOutbox + retry) for transient DynamoDB throttling","Verify table name/prefix config in integration tests before deploying","Keep saga DTOs System.Text.Json-serializable (public setters, supported types)"],"tags":["dynamodb","saga","persistence","masstransit","aws"],"backgroundTag":"database-write-failed","analyzedSha":"62ab339afa3bac2e9b3fe1769d0d35d7e44778e9","analyzedAt":"2026-09-13T22:47:47.593Z","contentChangedAt":"2026-09-13T22:47:47.593Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}