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

  1. Inspect the audit document's Content and repair or drop the corrupt entry.
  2. Align all writers on the same JsonSerializerOptions (Web defaults + JsonStringEnumConverter).
  3. 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

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


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)