elsa-workflows/elsa-core · error · DocumentStoreValidationException
Runtime entity audit document
Error message
Runtime entity audit document '{document.Id}' could not be deserialized. What it means
DeserializeAudit JSON-deserializes StoredDocument.Content into RuntimeEntityAuditRecord. A null result throws DocumentStoreValidationException naming the audit document id. Audit records are appended on every definition/instance mutation, so this indicates a stored audit entry that cannot be decoded.
Solutions
- Inspect the audit document's Content and repair or drop the corrupt entry.
- Align all writers on the same JsonSerializerOptions (Web defaults + JsonStringEnumConverter).
- Rebuild audit history from instance/definition state if the records are unrecoverable.
Example fix
// before
var audit = DeserializeAudit(doc); // throws on corrupt entry
// after
try { var audit = DeserializeAudit(doc); }
catch (DocumentStoreValidationException ex) {
logger.LogWarning(ex, "Skipping corrupt audit document {Id}", doc.Id);
continue; // audit read is best-effort
} Defensive patterns
Strategy: try-catch
Validate before calling
var doc = /* load StoredDocument */;
if (string.IsNullOrWhiteSpace(doc.Content) || doc.Content == "null")
return null; // skip unusable audit entry Type guard
bool IsValidAuditDocument(StoredDocument doc) =>
!string.IsNullOrWhiteSpace(doc.Content) && doc.Content != "null"; Try / catch
try { var audit = DeserializeAudit(doc); }
catch (DocumentStoreValidationException ex) { logger.LogWarning(ex, "Skipping audit document {Id}", doc.Id); } Prevention
- Treat audit reads as best-effort and skip corrupt entries rather than failing the whole trail.
- Keep audit writers on the same serializer configuration as readers.
- Include audit document integrity in backup/restore drills.
When it happens
Trigger: An audit document whose Content is null/empty/'null', was serialized with incompatible options (numeric enums, different naming policy), or was corrupted by external tooling or a failed migration.
Common situations: Reading audit trails after a serializer configuration change across versions; manual database cleanup; documents imported from another system; truncated writes during a crash.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- The persisted external authentication value could not be…
- Runtime entity definition document
- Runtime entity instance document
- The serialization type alias is missing.
- Unsupported console stream value
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/d8d0b0d08a7022c9.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.Persistence.VNext.Runtime/Services/RuntimeEntityManager.cs:244
return indexValues;
}
private RuntimeEntityDefinition DeserializeDefinition(StoredDocument document)
{
return JsonSerializer.Deserialize<RuntimeEntityDefinition>(document.Content, _jsonOptions)
?? throw new DocumentStoreValidationException($"Runtime entity definition document '{document.Id}' could not be deserialized.");
}
private RuntimeEntityInstance DeserializeInstance(StoredDocument document)
{
return JsonSerializer.Deserialize<RuntimeEntityInstance>(document.Content, _jsonOptions)
?? throw new DocumentStoreValidationException($"Runtime entity instance document '{document.Id}' could not be deserialized.");
}
private RuntimeEntityAuditRecord DeserializeAudit(StoredDocument document)
{
return JsonSerializer.Deserialize<RuntimeEntityAuditRecord>(document.Content, _jsonOptions)
?? throw new DocumentStoreValidationException($"Runtime entity audit document '{document.Id}' could not be deserialized.");
}
private static string NormalizeName(string name) => name.Trim().ToLowerInvariant();
private static string CreateInstanceDocumentId(string definitionName, string id) => $"{NormalizeName(definitionName)}:{id}";
private static string? ConvertIndexValue(object? value)
{
return value switch
{
null => null,
DateTimeOffset dateTimeOffset => dateTimeOffset.ToString("O", CultureInfo.InvariantCulture),
DateTime dateTime => dateTime.ToUniversalTime().ToString("O", CultureInfo.InvariantCulture),
IFormattable formattable => formattable.ToString(null, CultureInfo.InvariantCulture),
_ => value.ToString()
};
}
}
View on GitHub (pinned to fe9217bdfa)