clockworklabs/SpacetimeDB · error · InvalidOperationException
JWT missing or invalid 'iss' claim
Error message
JWT missing or invalid 'iss' claim
What it means
Thrown by the Issuer accessor of JwtClaims when the decoded JWT payload has no 'iss' claim or 'iss' is not a JSON string. Like Subject, parsing is lazy (Lazy<JsonDocument> over the payload string retrieved via FFI.get_jwt), so the exception occurs when the property is first read, not when the token arrives.
Source
Thrown at crates/bindings-csharp/Runtime/JwtClaims.cs:62
}
throw new InvalidOperationException("JWT missing or invalid 'sub' claim");
}
}
public string Issuer
{
get
{
if (
RootElement.TryGetProperty("iss", out var iss)
&& iss.ValueKind == JsonValueKind.String
)
{
return iss.GetString()!;
}
throw new InvalidOperationException("JWT missing or invalid 'iss' claim");
}
}
private List<string> ExtractAudience()
{
if (!RootElement.TryGetProperty("aud", out var aud))
{
return [];
}
return aud.ValueKind switch
{
JsonValueKind.String => [aud.GetString()!],
JsonValueKind.Array =>
[
.. aud.EnumerateArray()
.Where(e => e.ValueKind == JsonValueKind.String)
.Select(e => e.GetString()!),View on GitHub (pinned to 524b4487d9)
Solutions
- Inspect JwtClaims.RawPayload to confirm whether 'iss' exists and what type it is
- Fix the token issuer to emit a string 'iss' claim identifying who minted the token
- Read 'iss' defensively via JsonDocument.Parse on RawPayload if your module tolerates its absence
Example fix
// before
var issuer = ctx.Auth.Jwt!.Issuer; // throws if 'iss' missing/non-string
// after
using var doc = JsonDocument.Parse(ctx.Auth.Jwt!.RawPayload);
var issuer = doc.RootElement.TryGetProperty("iss", out var iss) && iss.ValueKind == JsonValueKind.String
? iss.GetString()!
: "<unknown-issuer>"; Defensive patterns
Strategy: validation
Validate before calling
bool HasStringIss(JwtClaims? jwt)
{
if (jwt == null) return false;
using var doc = JsonDocument.Parse(jwt.RawPayload);
return doc.RootElement.TryGetProperty("iss", out var i) && i.ValueKind == JsonValueKind.String;
} Type guard
static bool HasIssuer(JwtClaims? jwt) => jwt != null && HasStringIss(jwt);
Try / catch
try { var issuer = jwt.Issuer; }
catch (InvalidOperationException) { /* no string 'iss'; treat as untrusted/unknown issuer */ } Prevention
- Verify issuer presence with a payload probe before reading Issuer in hot paths
- Standardize on one token issuer so claim shape is predictable
- Write a unit test asserting your issuer's tokens contain string 'iss' and 'sub'
When it happens
Trigger: Reading authCtx.Jwt!.Issuer on a token whose payload lacks 'iss' or carries a non-string 'iss' (null, number, object). Tokens minted without the issuer claim trigger this on every read.
Common situations: Hand-crafted dev/test tokens that only carry custom claims; custom identity providers that skip issuer; multi-tenant setups where you expected 'iss' to identify the tenant but the issuer never set it.
Related errors
- JWT missing or invalid 'sub' claim
- Unexpected type for 'aud' claim in JWT
- Failed to verify token: ${response.statusText}
- Issuer too long: {:?}
- Subject too long: {:?}
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/af92890e02683dc3.
Report an issue: GitHub.