elsa-workflows/elsa-core · error · InvalidOperationException
The persisted external authentication value could not be…
Error message
The persisted external authentication value could not be deserialized as {typeof(T).Name}. What it means
Thrown by ExternalAuthenticationJsonSerializer.Deserialize<T> when the persisted string cannot be deserialized into T as a non-null value. The helper uses JsonSerializerDefaults.Web and treats a null result (JSON 'null' literal or shape mismatch producing null) as a hard failure, since a persisted external-authentication payload must always be a valid value of T.
Solutions
- Inspect the persisted string and re-serialize it to the expected type T (matching the Web defaults: camelCase, case-insensitive) before use, e.g. Serialize<T>(correctValue).
- If the row is stale from a schema change, migrate or delete the row and let the provisioning flow recreate it.
- Confirm T matches the type originally stored; deserialize to the legacy type first and map to the new type if a version change occurred.
- Guard callers: validate the raw value parses (JsonNode.Parse) and its shape matches T before invoking Deserialize.
Example fix
// before
var state = ExternalAuthenticationJsonSerializer.Deserialize<LinkState>(raw); // raw is 'null'
// after
var state = string.IsNullOrWhiteSpace(raw)
? null
: ExternalAuthenticationJsonSerializer.Deserialize<LinkState>(raw);
if (state is null) { /* recreate the link */ } Defensive patterns
Strategy: validation
Validate before calling
bool canDeserialize = !string.IsNullOrWhiteSpace(raw)
&& JsonNode.Parse(raw) is not null
&& JsonSerializer.Deserialize<T>(raw, new JsonSerializerOptions(JsonSerializerDefaults.Web)) is not null; Type guard
static bool IsDeserializableAs<T>(string? raw) where T : class
{
if (string.IsNullOrWhiteSpace(raw)) return false;
try { return JsonSerializer.Deserialize<T>(raw, new JsonSerializerOptions(JsonSerializerDefaults.Web)) is not null; }
catch (JsonException) { return false; }
} Try / catch
try { value = ExternalAuthenticationJsonSerializer.Deserialize<T>(raw); }
catch (InvalidOperationException ex) when (ex.Message.Contains("could not be deserialized"))
{
logger.LogWarning(ex, "Persisted payload corrupt; recreating");
value = null; // fall back to recreating the persisted state
} Prevention
- Always write persisted values through Serialize<T> so shapes stay consistent
- Round-trip Serialize/Deserialize in tests when changing payload types
- Migrate old rows before switching to a new payload type
- Reject NULL/garbage at the data-access layer before deserialization
When it happens
Trigger: Calling Deserialize<T> on a column value that is the JSON literal 'null', or whose JSON shape does not match T (e.g. stored JSON for a different type, empty object where a record with required properties is expected, or a value written by an older/different schema).
Common situations: EF Core external-authentication tables edited or truncated by hand; rows written by an older Elsa version before a payload-type change; copy/paste errors seeding the database; migrations that altered the payload shape without re-serializing existing rows.
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.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Runtime entity definition document
- Runtime entity instance document
- Runtime entity audit 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/060e270852fc637c.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.ExternalAuthentication.Persistence.EFCore/ExternalAuthenticationJsonSerializer.cs:12
using System.Text.Json;
namespace Elsa.ExternalAuthentication.Persistence.EFCore;
internal static class ExternalAuthenticationJsonSerializer
{
private static readonly JsonSerializerOptions Options = new(JsonSerializerDefaults.Web);
public static string Serialize<T>(T value) => JsonSerializer.Serialize(value, Options);
public static T Deserialize<T>(string value) => JsonSerializer.Deserialize<T>(value, Options)
?? throw new InvalidOperationException($"The persisted external authentication value could not be deserialized as {typeof(T).Name}.");
}
View on GitHub (pinned to fe9217bdfa)